You are viewing a plain text version of this content. The canonical link for it is here.
Posted to commits@airflow.apache.org by po...@apache.org on 2020/12/27 13:52:16 UTC

[airflow-cancel-workflow-runs] branch master created (now 953e057)

This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a change to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git.


      at 953e057  Fix case of running cancel for different workflow

This branch includes the following new commits:

     new 588db53  Initial impl
     new 8342de2  Add matrix
     new db63ce1  Fix pagination
     new 6595731  Add additional validation
     new 24356dc  Reenable branch filtering
     new e41960e  Update README.md
     new 40bee3e  Add support for pull requests
     new 28fd9a7  release
     new 3f0b5bc  Add initial support for schedule, and refactor accordingly
     new a38415f  optimize queries to only pull active statuses
     new beb7c0e  Also filteer non pr and push events
     new 70e4efc  Also filteer non pr and push events
     new 0802ecf  optimize queries to only pull active statuses
     new 3f732de  Update README.md
     new 35d6766  Update README.md
     new 022a420  Add branding info
     new 888f33b  spelling
     new 953c922  realease
     new 0180a5b  Update README.md
     new cc3b6f2  Bump acorn from 5.7.3 to 5.7.4
     new f4aa9bb  Bump @actions/http-client from 1.0.4 to 1.0.8
     new 7d3eeff  Merge pull request #2 from n1hility/dependabot/npm_and_yarn/acorn-5.7.4
     new de231f5  Merge pull request #3 from n1hility/dependabot/npm_and_yarn/actions/http-client-1.0.8
     new 6cc4a33  Add workflow parameter to inputs list
     new 2e3c189  Merge pull request #5 from jamesnetherton/fixup-warnings
     new f0bf36a  Adds cancelling of latest job when certain jobs failed
     new 9fd8616  Fixed docs
     new 2210b4b  Move to workflow_run triggered cancelling
     new 7dc5741  Fix example
     new 70a4586  Merge pull request #2 from TobKed/Fix-example
     new f054515  Add workflow_dispatch event to cancellable runs (#3)
     new f6b43a6  Fix pre commit failing on modified ts code (#4)
     new e9e87cb  Added functionality for PR retrieving and notifying. (#5)
     new 0ba85ca  Added optional message when cancelling workflow. (#6)
     new ca4b70a  Clarified README.
     new f696c62  Added capability of skipping some event types (#7)
     new 0acb1c0  Adds cancelling workflows by name (#8)
     new c8448eb  Add feature of cancelling future duplicates (enabled by default) (#9)
     new 99869d3  Adds 'allDuplicates' mode of cancelling (#10)
     new 19f8bd2  Adds allDuplicatedNamedJobs mode (#11)
     new f06d03c  Adds self-preservation mechanism. (#12)
     new 169f169  Revert problem with non-iterable data.jobs sometimes.
     new f4a3315  Fixed too-agressive (cross-branches) cancelling
     new 953e057  Fix case of running cancel for different workflow

The 44 revisions listed above as "new" are entirely new to this
repository and will be described in separate emails.  The revisions
listed as "add" were already present in the repository and have only
been added to this reference.



[airflow-cancel-workflow-runs] 36/44: Added capability of skipping some event types (#7)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f696c622a83e4a63fff74848d3b149074658607d
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Oct 10 15:11:03 2020 +0200

    Added capability of skipping some event types (#7)
---
 README.md     |  6 ++++++
 action.yml    |  5 +++++
 dist/index.js | 24 +++++++++++++++++-------
 src/main.ts   | 30 +++++++++++++++++++++++++-----
 4 files changed, 53 insertions(+), 12 deletions(-)

diff --git a/README.md b/README.md
index 9de025f..5354a8e 100644
--- a/README.md
+++ b/README.md
@@ -114,6 +114,8 @@ and `schedule` events are no longer needed.
 | `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
 | `notifyPRMessageStart`  | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
 | `jobNameRegexps`        | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+| `skipEventTypes`        | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events..                                                       |
+
 
 The job cancel modes work as follows:
 
@@ -303,6 +305,9 @@ This works for all kind of triggering events (`push`, `pull_request`, `schedule`
 events triggered in the local repository, as well as triggered from the forks, so you do not need
 to set up any extra actions to cancel internal Pushes/Pull Requests.
 
+You can also choose to skip certain types of events (for example `push` and `schedule` if you want your
+jobs to run to full completion for this kind of events.
+
 ```yaml
 name: Cancelling
 on:
@@ -322,6 +327,7 @@ jobs:
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
           notifyPRCancel: true
+          skipEventTypes: '["push", "schedule"]'
 ```
 
 Note that `duplicate` cancel mode cannot be used for `workflow_run` type of event without `sourceId` input.
diff --git a/action.yml b/action.yml
index 7accb50..8fc9087 100644
--- a/action.yml
+++ b/action.yml
@@ -41,6 +41,11 @@ inputs:
       Array of job name regexps (JSON-encoded string). Used by `failedJobs` and `namedJobs` cancel modes
       to match job names of workflow runs.
     required: false
+  skipEventTypes:
+    description: |
+      Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used
+      in order to skip direct pushes or scheduled events.
+    required: false
 runs:
   using: 'node12'
   main: 'dist/index.js'
diff --git a/dist/index.js b/dist/index.js
index 7f28bf7..53c47fc 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1622,7 +1622,7 @@ function getWorkflowRuns(octokit, statusValues, cancelMode, createListRunQuery)
         return workflowRuns;
     });
 }
-function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNamesRegexps) {
+function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNamesRegexps, skipEventTypes) {
     return __awaiter(this, void 0, void 0, function* () {
         if ('completed' === runItem.status.toString()) {
             core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
@@ -1632,6 +1632,11 @@ function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode,
             core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not in ${CANCELLABLE_RUNS}). Not cancelling it.\n`);
             return false;
         }
+        if (skipEventTypes.includes(runItem.event.toString())) {
+            core.info(`\nThe run ${runItem.id} is (${runItem.event} event - ` +
+                `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`);
+            return false;
+        }
         if (cancelMode === CancelMode.FAILED_JOBS) {
             // Cancel all jobs that have failed jobs (no matter when started)
             if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, true)) {
@@ -1705,7 +1710,7 @@ function cancelRun(octokit, owner, repo, runId) {
         }
     });
 }
-function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, reason) {
+function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
         const workflowRuns = yield getWorkflowRuns(octokit, statusValues, cancelMode, function (status) {
@@ -1734,7 +1739,7 @@ function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, ow
         const pullRequestToNotify = [];
         for (const [key, runItem] of workflowRuns) {
             core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`);
-            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNameRegexps)) {
+            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNameRegexps, skipEventTypes)) {
                 if (notifyPRCancel && runItem.event === 'pull_request') {
                     const pullRequest = yield findPullRequest(octokit, owner, repo, runItem.head_repository.owner.login, runItem.head_branch, runItem.head_sha);
                     if (pullRequest) {
@@ -1832,7 +1837,7 @@ function getOrigin(octokit, runId, owner, repo) {
         ];
     });
 }
-function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps) {
+function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
@@ -1862,7 +1867,7 @@ function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, own
             throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, reason);
+        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
     });
 }
 function verboseOutput(name, value) {
@@ -1885,8 +1890,13 @@ function run() {
         const jobNameRegexps = jobNameRegexpsString
             ? JSON.parse(jobNameRegexpsString)
             : [];
+        const skipEventTypesString = core.getInput('skipEventTypes');
+        const skipEventTypes = skipEventTypesString
+            ? JSON.parse(skipEventTypesString)
+            : [];
         const [owner, repo] = repository.split('/');
-        core.info(`\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo}\n`);
+        core.info(`\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
+            ` skipEventTypes: ${skipEventTypes}\n`);
         const sourceWorkflowId = yield getWorkflowId(octokit, sourceRunId, owner, repo);
         core.info(`Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
             `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
@@ -1926,7 +1936,7 @@ function run() {
                 body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
             });
         }
-        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps);
+        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index 44ef1bc..e7bcbe8 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -204,7 +204,8 @@ async function shouldBeCancelled(
   headRepo: string,
   cancelMode: CancelMode,
   sourceRunId: number,
-  jobNamesRegexps: string[]
+  jobNamesRegexps: string[],
+  skipEventTypes: string[]
 ): Promise<boolean> {
   if ('completed' === runItem.status.toString()) {
     core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`)
@@ -216,6 +217,13 @@ async function shouldBeCancelled(
     )
     return false
   }
+  if (skipEventTypes.includes(runItem.event.toString())) {
+    core.info(
+      `\nThe run ${runItem.id} is (${runItem.event} event - ` +
+        `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`
+    )
+    return false
+  }
   if (cancelMode === CancelMode.FAILED_JOBS) {
     // Cancel all jobs that have failed jobs (no matter when started)
     if (
@@ -333,6 +341,7 @@ async function findAndCancelRuns(
   notifyPRCancel: boolean,
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
+  skipEventTypes: string[],
   reason: string
 ): Promise<number[]> {
   const statusValues = ['queued', 'in_progress']
@@ -406,7 +415,8 @@ async function findAndCancelRuns(
         headRepo,
         cancelMode,
         sourceRunId,
-        jobNameRegexps
+        jobNameRegexps,
+        skipEventTypes
       )
     ) {
       if (notifyPRCancel && runItem.event === 'pull_request') {
@@ -570,7 +580,8 @@ async function performCancelJob(
   notifyPRCancel: boolean,
   notifyPRCancelMessage: string,
   notifyPRMessageStart: string,
-  jobNameRegexps: string[]
+  jobNameRegexps: string[],
+  skipEventTypes: string[]
 ): Promise<number[]> {
   core.info(
     '\n###################################################################################\n'
@@ -627,6 +638,7 @@ async function performCancelJob(
     notifyPRCancel,
     notifyPRMessageStart,
     jobNameRegexps,
+    skipEventTypes,
     reason
   )
 }
@@ -653,10 +665,17 @@ async function run(): Promise<void> {
   const jobNameRegexps = jobNameRegexpsString
     ? JSON.parse(jobNameRegexpsString)
     : []
+
+  const skipEventTypesString = core.getInput('skipEventTypes')
+  const skipEventTypes = skipEventTypesString
+    ? JSON.parse(skipEventTypesString)
+    : []
+
   const [owner, repo] = repository.split('/')
 
   core.info(
-    `\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo}\n`
+    `\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
+      ` skipEventTypes: ${skipEventTypes}\n`
   )
   const sourceWorkflowId = await getWorkflowId(
     octokit,
@@ -738,7 +757,8 @@ async function run(): Promise<void> {
     notifyPRCancel,
     notifyPRCancelMessage,
     notifyPRMessageStart,
-    jobNameRegexps
+    jobNameRegexps,
+    skipEventTypes
   )
 
   verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))


[airflow-cancel-workflow-runs] 21/44: Bump @actions/http-client from 1.0.4 to 1.0.8

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f4aa9bbae09d3063b190dd9ee1676cb3d768c45f
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Wed Apr 29 18:01:23 2020 +0000

    Bump @actions/http-client from 1.0.4 to 1.0.8
    
    Bumps [@actions/http-client](https://github.com/actions/http-client) from 1.0.4 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] <su...@github.com>
---
 package-lock.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 121b6fc..8b8bf9d 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -20,9 +20,9 @@
       }
     },
     "@actions/http-client": {
-      "version": "1.0.4",
-      "resolved": "https://registry.npmjs.org/@actions/http-client/-/http-client-1.0.4.tgz",
-      "integrity": "sha512-6EzXhqapKKtYr21ZnFQVBYwfrYPKPCivuSkUN/66/BDakkH2EPjUZH8tZ3MgHdI+gQIdcsY0ybbxw9ZEOmJB6g==",
+      "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"
       }


[airflow-cancel-workflow-runs] 35/44: Clarified README.

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit ca4b70a6910d33990e16d95e0c116914cdc0dfd0
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Wed Oct 7 18:26:55 2020 +0200

    Clarified README.
---
 README.md  | 4 ++--
 action.yml | 3 ++-
 2 files changed, 4 insertions(+), 3 deletions(-)

diff --git a/README.md b/README.md
index a2fcca2..9de025f 100644
--- a/README.md
+++ b/README.md
@@ -111,7 +111,7 @@ and `schedule` events are no longer needed.
 | `cancelMode`            | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
 | `sourceRunId`           | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
 | `notifyPRCancel`        | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
-| `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.                                                                                                                           |
+| `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
 | `notifyPRMessageStart`  | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
 | `jobNameRegexps`        | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
 
@@ -381,7 +381,6 @@ jobs:
           cancelMode: duplicates
           token: ${{ secrets.GITHUB_TOKEN }}
           notifyPRCancel: true
-          notifyPRCancelMessage: Cancelled because image building failed.
           notifyPRMessageStart: |
             Note! The Docker Images for the build are prepared in a separate workflow,
             that you will not see in the list of checks.
@@ -441,6 +440,7 @@ on:
         with:
           cancelMode: self
           notifyPRCancel: true
+          notifyPRCancelMessage: Cancelled because image building failed.
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
 ```
diff --git a/action.yml b/action.yml
index 0666499..7accb50 100644
--- a/action.yml
+++ b/action.yml
@@ -18,7 +18,8 @@ inputs:
     required: false
   notifyPRCancelMessage:
     description: |
-      Optional cancel message to use instead of the default one when notifyPRCancel is true.
+      Optional cancel message to use instead of the default one when notifyPRCancel is true. Only
+      used in 'self' cancel mode.
     required: false
   notifyPRMessageStart:
     description: |


[airflow-cancel-workflow-runs] 02/44: Add matrix

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 8342de2ac3b7a70154b024b6a99756d76510165a
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 4 00:38:16 2020 -0600

    Add matrix
---
 .github/workflows/test.yml | 9 ++++++++-
 1 file changed, 8 insertions(+), 1 deletion(-)

diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index afdd881..61c2baf 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -12,7 +12,14 @@ jobs:
         npm install
         npm run all
   test: # make sure the action works on a clean machine without building
-    runs-on: ubuntu-latest
+    name: Test on ${{ matrix.os }}
+
+    strategy:
+      matrix:
+        os: [ubuntu-latest, windows-latest, macOS-latest]
+
+    runs-on: ${{ matrix.os }}
+    
     steps:
     - uses: actions/checkout@v1
     - uses: ./


[airflow-cancel-workflow-runs] 33/44: Added functionality for PR retrieving and notifying. (#5)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit e9e87cb7738dbb999654aa90d69359d62c9e4eae
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Oct 4 21:57:04 2020 +0200

    Added functionality for PR retrieving and notifying. (#5)
    
    In case the workflow was triggered by a Pull Request,
    it finds out the PR that triggered it and notifies it (controlled
    by the parameters specified):
    
    * when the workflow is started with specified message
    * when any other PR is cancelled, it is notificed with
      explainiing the reason why it has been cancelled.
---
 README.md     |  61 ++++++++++++++++------
 action.yml    |  10 ++++
 dist/index.js |  96 ++++++++++++++++++++++++++++++----
 src/main.ts   | 162 +++++++++++++++++++++++++++++++++++++++++++++++++++++-----
 4 files changed, 288 insertions(+), 41 deletions(-)

diff --git a/README.md b/README.md
index 021e34f..7946ea9 100644
--- a/README.md
+++ b/README.md
@@ -69,6 +69,14 @@ resolved during execution of the workflow using information about the linked wor
 at the workflow runtime. Hopefully this information will soon be available in GitHub Actions allowing
 removal of `namedJobs` cancel mode and simplifying the examples and workflows using the Action.
 
+Another feature of the Action is to notify the PRs linked to the workflows. Normally when workflows
+get cancelled there is no information why it happens, but this action can add an explanatory comment
+to the PR if the PR gets cancelled. This is controlled by `notifyPRCancel` boolean input.
+
+Also, for the `workflow_run` events, GitHub does not yet provide an easy interface linking the original
+Pull Request and the Workflow_run. You can ask the CancelWorkflowRun action to add extra comment to the PR
+adding explanatory message followed by a link to the `workflow_run` run.
+
 You can take a look at the description provided in the
 [Apache Airflow's CI](https://github.com/apache/airflow/blob/master/CI.rst) and
 [the workflows](https://github.com/apache/airflow/blob/master/.github/workflows)
@@ -97,12 +105,14 @@ and `schedule` events are no longer needed.
 
 ## Inputs
 
-| Input            | Required | Default      | Comment                                                                                                                                                                                                          |
-|------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `token`          | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
-| `cancelMode`     | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
-| `sourceRunId`    | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
-| `jobNameRegexps` | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+| Input                  | Required | Default      | Comment                                                                                                                                                                                                          |
+|------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `token`                | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
+| `cancelMode`           | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
+| `sourceRunId`          | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
+| `notifyPRCancel`       | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
+| `notifyPRMessageStart` | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
+| `jobNameRegexps`       | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
 
 The job cancel modes work as follows:
 
@@ -116,13 +126,16 @@ The job cancel modes work as follows:
 
 ## Outputs
 
-| Output             | No `sourceRunId` specified                   | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                                      |
-|--------------------|----------------------------------------------|-----------------------------------------------------------------------------------------------------|
-| `sourceHeadRepo`   | Current repository. Format: `owner/repo`     | Repository of the run that triggered this `workflow_run`. Format: `owner/repo`                      |
-| `sourceHeadBranch` | Current branch.                              | Branch of the run that triggered this `workflow_run`. Might be forked repo, if it is a pull_requst. |
-| `sourceHeadSha`    | Current commit SHA: `{{ github.sha }}`       | Commit sha of the run that triggered this `workflow_run`.                                           |
-| `sourceEvent`      | Current event: ``${{ github.event }}``       | Event of the run that triggered this `workflow_run`                                                 |
-| `cancelledRuns`    | JSON-stringified array of cancelled run ids. | JSON-stringified array of cancelled run ids.                                                        |
+| Output              | No `sourceRunId` specified                              | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                                       |
+|---------------------|---------------------------------------------------------|------------------------------------------------------------------------------------------------------|
+| `sourceHeadRepo`    | Current repository. Format: `owner/repo`                | Repository of the run that triggered this `workflow_run`. Format: `owner/repo`                       |
+| `sourceHeadBranch`  | Current branch.                                         | Branch of the run that triggered this `workflow_run`. Might be forked repo, if it is a pull_request. |
+| `sourceHeadSha`     | Current commit SHA: `{{ github.sha }}`                  | Commit sha of the run that triggered this `workflow_run`.                                            |
+| `mergeCommitSha`    | Merge commit SHA if PR-triggered event.                 | Merge commit SHA if PR-triggered event.                                                              |
+| `targetCommitSha`   | Target commit SHA (merge if present, otherwise source). | Target commit SHA (merge if present, otherwise source).                                              |
+| `pullRequestNumber` | Number of the associated Pull Request (if PR triggered) | Number of the associated Pull Request (if PR triggered)                                              |
+| `sourceEvent`       | Current event: ``${{ github.event }}``                  | Event of the run that triggered this `workflow_run`                                                  |
+| `cancelledRuns`     | JSON-stringified array of cancelled run ids.            | JSON-stringified array of cancelled run ids.                                                         |
 
 # Examples
 
@@ -149,7 +162,8 @@ Cancels past runs for the same workflow (with the same branch).
 
 In the case below, any of the direct "push" events will cancel all past runs for the same branch as the
 one being pushed. However, it can be configured for "pull_request" (in the same repository) or "schedule"
-type of events as well.
+type of events as well. It will also notify the PR with the comment containining why it has been
+cancelled.
 
 ```yaml
 name: CI
@@ -163,7 +177,7 @@ jobs:
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
-          token: ${{ secrets.GITHUB_TOKEN }}
+          notifyPRCancel: true
 ```
 
 ### Cancel "self" workflow run
@@ -186,7 +200,7 @@ jobs:
         with:
           cancelMode: self
           token: ${{ secrets.GITHUB_TOKEN }}
-
+          notifyPRCancel: true
 ```
 
 ### Fail-fast workflow runs with failed jobs
@@ -214,6 +228,7 @@ jobs:
           cancelMode: failedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+          notifyPRCancel: true
 ```
 
 ### Cancel all runs with named jobs
@@ -245,6 +260,7 @@ jobs:
           cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+          notifyPRCancel: true
 ```
 
 ## Repositories that use Pull Requests from forks
@@ -304,6 +320,7 @@ jobs:
           cancelMode: duplicates
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
+          notifyPRCancel: true
 ```
 
 Note that `duplicate` cancel mode cannot be used for `workflow_run` type of event without `sourceId` input.
@@ -362,11 +379,18 @@ jobs:
         with:
           cancelMode: duplicates
           token: ${{ secrets.GITHUB_TOKEN }}
+          notifyPRCancel: true
+          notifyPRMessageStart: |
+            Note! The Docker Images for the build are prepared in a separate workflow,
+            that you will not see in the list of checks.
+
+            You can checks the status of those images in:
       - uses: potiuk/cancel-workflow-runs@v2
         name: "Cancel duplicate Cancelling runs"
         with:
           cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
+          notifyPRCancel: true
           jobNameRegexps: >
             ["Build info
             repo: ${{ steps.cancel.outputs.sourceHeadRepo }}
@@ -414,6 +438,7 @@ on:
         uses: potiuk/cancel-workflow-runs@v2
         with:
           cancelMode: self
+          notifyPRCancel: true
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
 ```
@@ -441,6 +466,7 @@ on:
         uses: potiuk/cancel-workflow-runs@v2
         with:
           cancelMode: self
+          notifyPRCancel: true
           token: ${{ secrets.GITHUB_TOKEN }}
 
 ```
@@ -476,6 +502,7 @@ jobs:
           cancelMode: failedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
+          notifyPRCancel: true
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
 ```
 
@@ -517,6 +544,7 @@ jobs:
           cancelMode: failedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
+          notifyPRCancel: true
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
       - name: "Extract canceled failed runs"
         id: extract-cancelled-failed-runs
@@ -536,6 +564,7 @@ jobs:
         with:
           cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
+          notifyPRCancel: true
           jobNameRegexps: ${{ steps.extract-cancelled-failed.runs.matching-regexp }}
 
 ```
diff --git a/action.yml b/action.yml
index 71f6ae2..932b45f 100644
--- a/action.yml
+++ b/action.yml
@@ -11,6 +11,16 @@ inputs:
       `$\{\{ github.event.workflow_run.id` variable \}\}` if used in `workflow_run` triggered run if
       you want to act on source workflow rather than the triggered run.
     required: false
+  notifyPRCancel:
+    description: |
+      Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why
+      they are being cancelled.
+    required: false
+  notifyPRMessageStart:
+    description: |
+      Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the
+      message specified at the start of the workflow - adding the link to the triggered workflow_run.
+    required: false
   cancelMode:
     description: |
       The mode of cancel. One of:
diff --git a/dist/index.js b/dist/index.js
index 3c03be4..a7796dd 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1705,7 +1705,7 @@ function cancelRun(octokit, owner, repo, runId) {
         }
     });
 }
-function findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps) {
+function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
         const workflowRuns = yield getWorkflowRuns(octokit, statusValues, cancelMode, function (status) {
@@ -1731,9 +1731,16 @@ function findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo,
             }
         });
         const idsToCancel = [];
+        const pullRequestToNotify = [];
         for (const [key, runItem] of workflowRuns) {
             core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`);
             if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNameRegexps)) {
+                if (notifyPRCancel && runItem.event === 'pull_request') {
+                    const pullRequest = yield findPullRequest(octokit, owner, repo, runItem.head_repository.owner.login, runItem.head_branch, runItem.head_sha);
+                    if (pullRequest) {
+                        pullRequestToNotify.push(pullRequest.number);
+                    }
+                }
                 idsToCancel.push(runItem.id);
             }
         }
@@ -1741,11 +1748,16 @@ function findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo,
         const sortedIdsToCancel = idsToCancel.sort((id1, id2) => id1 - id2);
         if (sortedIdsToCancel.length > 0) {
             core.info('\n######  Cancelling runs starting from the oldest  ##########\n' +
-                `\n     Runs to cancel: ${sortedIdsToCancel.length}\n`);
+                `\n     Runs to cancel: ${sortedIdsToCancel.length}\n` +
+                `\n     PRs to notify: ${pullRequestToNotify.length}\n`);
             for (const runId of sortedIdsToCancel) {
                 core.info(`\nCancelling run: ${runId}.\n`);
                 yield cancelRun(octokit, owner, repo, runId);
             }
+            for (const pullRequestNumber of pullRequestToNotify) {
+                const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
+                yield addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, `[The Build Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`);
+            }
             core.info('\n######  Finished cancelling runs                  ##########\n');
         }
         else {
@@ -1762,6 +1774,38 @@ function getRequiredEnv(key) {
     }
     return value;
 }
+function addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, comment) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info(`\nNotifying PR: ${pullRequestNumber} with '${comment}'.\n`);
+        yield octokit.issues.createComment({
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            issue_number: pullRequestNumber,
+            body: comment
+        });
+    });
+}
+function findPullRequest(octokit, owner, repo, headRepo, headBranch, headSha) {
+    return __awaiter(this, void 0, void 0, function* () {
+        // Finds Pull request for this workflow run
+        core.info(`\nFinding PR request id for: owner: ${owner}, Repo:${repo}, Head:${headRepo}:${headBranch}.\n`);
+        const pullRequests = yield octokit.pulls.list({
+            owner,
+            repo,
+            head: `${headRepo}:${headBranch}`
+        });
+        for (const pullRequest of pullRequests.data) {
+            core.info(`\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`);
+            if (pullRequest.head.sha === headSha) {
+                core.info(`\nFound PR: ${pullRequest.number}\n`);
+                return pullRequest;
+            }
+        }
+        core.info(`\nCould not find the PR for this build :(\n`);
+        return null;
+    });
+}
 function getOrigin(octokit, runId, owner, repo) {
     return __awaiter(this, void 0, void 0, function* () {
         const reply = yield octokit.actions.getWorkflowRun({
@@ -1774,40 +1818,55 @@ function getOrigin(octokit, runId, owner, repo) {
         core.info(`Source workflow: Head repo: ${sourceRun.head_repository.full_name}, ` +
             `Head branch: ${sourceRun.head_branch} ` +
             `Event: ${sourceRun.event}, Head sha: ${sourceRun.head_sha}, url: ${sourceRun.url}`);
+        let pullRequest = null;
+        if (sourceRun.event === 'pull_request') {
+            pullRequest = yield findPullRequest(octokit, owner, repo, sourceRun.head_repository.owner.login, sourceRun.head_branch, sourceRun.head_sha);
+        }
         return [
             reply.data.head_repository.full_name,
             reply.data.head_branch,
             reply.data.event,
-            reply.data.head_sha
+            reply.data.head_sha,
+            pullRequest ? pullRequest.merge_commit_sha : '',
+            pullRequest
         ];
     });
 }
-function performCancelJob(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps) {
+function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
             `head repo ${headRepo}, headBranch: ${headBranch}, ` +
             `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
         core.info('\n###################################################################################\n');
+        let reason = '';
         if (cancelMode === CancelMode.SELF) {
             core.info(`# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+            reason = `The job has been cancelled by another workflow.`;
         }
         else if (cancelMode === CancelMode.FAILED_JOBS) {
             core.info(`# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`);
+            reason = `It has some failed jobs matching ${jobNameRegexps}.`;
         }
         else if (cancelMode === CancelMode.NAMED_JOBS) {
             core.info(`# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`);
+            reason = `It has jobs matching ${jobNameRegexps}.`;
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
             core.info(`# Cancel duplicate runs started before ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+            reason = `It in earlier duplicate of ${sourceWorkflowId} run.`;
         }
         else {
             throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps);
+        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, reason);
     });
 }
+function verboseOutput(name, value) {
+    core.info(`Setting output: ${name}: ${value}`);
+    core.setOutput(name, value);
+}
 function run() {
     return __awaiter(this, void 0, void 0, function* () {
         const token = core.getInput('token', { required: true });
@@ -1816,6 +1875,8 @@ function run() {
         const repository = getRequiredEnv('GITHUB_REPOSITORY');
         const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
         const cancelMode = core.getInput('cancelMode') || CancelMode.DUPLICATES;
+        const notifyPRCancel = (core.getInput('notifyPRCancel') || 'false').toLowerCase() === 'true';
+        const notifyPRMessageStart = core.getInput('notifyPRMessageStart');
         const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
         const jobNameRegexpsString = core.getInput('jobNameRegexps');
         const jobNameRegexps = jobNameRegexpsString
@@ -1844,12 +1905,25 @@ function run() {
                     'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
                     'See the docs for details.');
         }
-        const [headRepo, headBranch, sourceEventName, headSha] = yield getOrigin(octokit, sourceRunId, owner, repo);
-        core.setOutput('sourceHeadRepo', headRepo);
-        core.setOutput('sourceHeadBranch', headBranch);
-        core.setOutput('sourceHeadSha', headSha);
-        core.setOutput('sourceEvent', sourceEventName);
-        const cancelledRuns = yield performCancelJob(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps);
+        const [headRepo, headBranch, sourceEventName, headSha, mergeCommitSha, pullRequest] = yield getOrigin(octokit, sourceRunId, owner, repo);
+        verboseOutput('sourceHeadRepo', headRepo);
+        verboseOutput('sourceHeadBranch', headBranch);
+        verboseOutput('sourceHeadSha', headSha);
+        verboseOutput('sourceEvent', sourceEventName);
+        verboseOutput('pullRequestNumber', pullRequest ? pullRequest.number.toString() : '');
+        verboseOutput('mergeCommitSha', mergeCommitSha);
+        verboseOutput('targetCommitSha', pullRequest ? mergeCommitSha : headSha);
+        const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
+        if (notifyPRMessageStart && pullRequest) {
+            yield octokit.issues.createComment({
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                issue_number: pullRequest.number,
+                body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
+            });
+        }
+        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps);
         core.setOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index 01a8a9d..056ccb8 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -321,6 +321,7 @@ async function cancelRun(
 
 async function findAndCancelRuns(
   octokit: github.GitHub,
+  selfRunId: number,
   sourceWorkflowId: number,
   sourceRunId: number,
   owner: string,
@@ -329,7 +330,10 @@ async function findAndCancelRuns(
   headBranch: string,
   sourceEventName: string,
   cancelMode: CancelMode,
-  jobNameRegexps: string[]
+  notifyPRCancel: boolean,
+  notifyPRMessageStart: string,
+  jobNameRegexps: string[],
+  reason: string
 ): Promise<number[]> {
   const statusValues = ['queued', 'in_progress']
   const workflowRuns = await getWorkflowRuns(
@@ -388,6 +392,7 @@ async function findAndCancelRuns(
     }
   )
   const idsToCancel: number[] = []
+  const pullRequestToNotify: number[] = []
   for (const [key, runItem] of workflowRuns) {
     core.info(
       `\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`
@@ -404,6 +409,19 @@ async function findAndCancelRuns(
         jobNameRegexps
       )
     ) {
+      if (notifyPRCancel && runItem.event === 'pull_request') {
+        const pullRequest = await findPullRequest(
+          octokit,
+          owner,
+          repo,
+          runItem.head_repository.owner.login,
+          runItem.head_branch,
+          runItem.head_sha
+        )
+        if (pullRequest) {
+          pullRequestToNotify.push(pullRequest.number)
+        }
+      }
       idsToCancel.push(runItem.id)
     }
   }
@@ -412,12 +430,23 @@ async function findAndCancelRuns(
   if (sortedIdsToCancel.length > 0) {
     core.info(
       '\n######  Cancelling runs starting from the oldest  ##########\n' +
-        `\n     Runs to cancel: ${sortedIdsToCancel.length}\n`
+        `\n     Runs to cancel: ${sortedIdsToCancel.length}\n` +
+        `\n     PRs to notify: ${pullRequestToNotify.length}\n`
     )
     for (const runId of sortedIdsToCancel) {
       core.info(`\nCancelling run: ${runId}.\n`)
       await cancelRun(octokit, owner, repo, runId)
     }
+    for (const pullRequestNumber of pullRequestToNotify) {
+      const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`
+      await addCommentToPullRequest(
+        octokit,
+        owner,
+        repo,
+        pullRequestNumber,
+        `[The Build Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`
+      )
+    }
     core.info(
       '\n######  Finished cancelling runs                  ##########\n'
     )
@@ -438,12 +467,61 @@ function getRequiredEnv(key: string): string {
   return value
 }
 
+async function addCommentToPullRequest(
+  octokit: github.GitHub,
+  owner: string,
+  repo: string,
+  pullRequestNumber: number,
+  comment: string
+): Promise<void> {
+  core.info(`\nNotifying PR: ${pullRequestNumber} with '${comment}'.\n`)
+  await octokit.issues.createComment({
+    owner,
+    repo,
+    // eslint-disable-next-line @typescript-eslint/camelcase
+    issue_number: pullRequestNumber,
+    body: comment
+  })
+}
+
+async function findPullRequest(
+  octokit: github.GitHub,
+  owner: string,
+  repo: string,
+  headRepo: string,
+  headBranch: string,
+  headSha: string
+): Promise<rest.PullsListResponseItem | null> {
+  // Finds Pull request for this workflow run
+  core.info(
+    `\nFinding PR request id for: owner: ${owner}, Repo:${repo}, Head:${headRepo}:${headBranch}.\n`
+  )
+  const pullRequests = await octokit.pulls.list({
+    owner,
+    repo,
+    head: `${headRepo}:${headBranch}`
+  })
+  for (const pullRequest of pullRequests.data) {
+    core.info(
+      `\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`
+    )
+    if (pullRequest.head.sha === headSha) {
+      core.info(`\nFound PR: ${pullRequest.number}\n`)
+      return pullRequest
+    }
+  }
+  core.info(`\nCould not find the PR for this build :(\n`)
+  return null
+}
+
 async function getOrigin(
   octokit: github.GitHub,
   runId: number,
   owner: string,
   repo: string
-): Promise<[string, string, string, string]> {
+): Promise<
+  [string, string, string, string, string, rest.PullsListResponseItem | null]
+> {
   const reply = await octokit.actions.getWorkflowRun({
     owner,
     repo,
@@ -456,16 +534,31 @@ async function getOrigin(
       `Head branch: ${sourceRun.head_branch} ` +
       `Event: ${sourceRun.event}, Head sha: ${sourceRun.head_sha}, url: ${sourceRun.url}`
   )
+  let pullRequest: rest.PullsListResponseItem | null = null
+  if (sourceRun.event === 'pull_request') {
+    pullRequest = await findPullRequest(
+      octokit,
+      owner,
+      repo,
+      sourceRun.head_repository.owner.login,
+      sourceRun.head_branch,
+      sourceRun.head_sha
+    )
+  }
+
   return [
     reply.data.head_repository.full_name,
     reply.data.head_branch,
     reply.data.event,
-    reply.data.head_sha
+    reply.data.head_sha,
+    pullRequest ? pullRequest.merge_commit_sha : '',
+    pullRequest
   ]
 }
 
 async function performCancelJob(
   octokit: github.GitHub,
+  selfRunId: number,
   sourceWorkflowId: number,
   sourceRunId: number,
   owner: string,
@@ -474,6 +567,8 @@ async function performCancelJob(
   headBranch: string,
   sourceEventName: string,
   cancelMode: CancelMode,
+  notifyPRCancel: boolean,
+  notifyPRMessageStart: string,
   jobNameRegexps: string[]
 ): Promise<number[]> {
   core.info(
@@ -487,22 +582,27 @@ async function performCancelJob(
   core.info(
     '\n###################################################################################\n'
   )
+  let reason = ''
   if (cancelMode === CancelMode.SELF) {
     core.info(
       `# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`
     )
+    reason = `The job has been cancelled by another workflow.`
   } else if (cancelMode === CancelMode.FAILED_JOBS) {
     core.info(
       `# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`
     )
+    reason = `It has some failed jobs matching ${jobNameRegexps}.`
   } else if (cancelMode === CancelMode.NAMED_JOBS) {
     core.info(
       `# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`
     )
+    reason = `It has jobs matching ${jobNameRegexps}.`
   } else if (cancelMode === CancelMode.DUPLICATES) {
     core.info(
       `# Cancel duplicate runs started before ${sourceRunId} for workflow ${sourceWorkflowId}.`
     )
+    reason = `It in earlier duplicate of ${sourceWorkflowId} run.`
   } else {
     throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`)
   }
@@ -512,6 +612,7 @@ async function performCancelJob(
 
   return await findAndCancelRuns(
     octokit,
+    selfRunId,
     sourceWorkflowId,
     sourceRunId,
     owner,
@@ -520,10 +621,18 @@ async function performCancelJob(
     headBranch,
     sourceEventName,
     cancelMode,
-    jobNameRegexps
+    notifyPRCancel,
+    notifyPRMessageStart,
+    jobNameRegexps,
+    reason
   )
 }
 
+function verboseOutput(name: string, value: string): void {
+  core.info(`Setting output: ${name}: ${value}`)
+  core.setOutput(name, value)
+}
+
 async function run(): Promise<void> {
   const token = core.getInput('token', {required: true})
   const octokit = new github.GitHub(token)
@@ -532,6 +641,9 @@ async function run(): Promise<void> {
   const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
   const cancelMode =
     (core.getInput('cancelMode') as CancelMode) || CancelMode.DUPLICATES
+  const notifyPRCancel =
+    (core.getInput('notifyPRCancel') || 'false').toLowerCase() === 'true'
+  const notifyPRMessageStart = core.getInput('notifyPRMessageStart')
   const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId
   const jobNameRegexpsString = core.getInput('jobNameRegexps')
   const jobNameRegexps = jobNameRegexpsString
@@ -577,20 +689,40 @@ async function run(): Promise<void> {
       )
   }
 
-  const [headRepo, headBranch, sourceEventName, headSha] = await getOrigin(
-    octokit,
-    sourceRunId,
-    owner,
-    repo
+  const [
+    headRepo,
+    headBranch,
+    sourceEventName,
+    headSha,
+    mergeCommitSha,
+    pullRequest
+  ] = await getOrigin(octokit, sourceRunId, owner, repo)
+
+  verboseOutput('sourceHeadRepo', headRepo)
+  verboseOutput('sourceHeadBranch', headBranch)
+  verboseOutput('sourceHeadSha', headSha)
+  verboseOutput('sourceEvent', sourceEventName)
+  verboseOutput(
+    'pullRequestNumber',
+    pullRequest ? pullRequest.number.toString() : ''
   )
+  verboseOutput('mergeCommitSha', mergeCommitSha)
+  verboseOutput('targetCommitSha', pullRequest ? mergeCommitSha : headSha)
 
-  core.setOutput('sourceHeadRepo', headRepo)
-  core.setOutput('sourceHeadBranch', headBranch)
-  core.setOutput('sourceHeadSha', headSha)
-  core.setOutput('sourceEvent', sourceEventName)
+  const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`
+  if (notifyPRMessageStart && pullRequest) {
+    await octokit.issues.createComment({
+      owner,
+      repo,
+      // eslint-disable-next-line @typescript-eslint/camelcase
+      issue_number: pullRequest.number,
+      body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
+    })
+  }
 
   const cancelledRuns = await performCancelJob(
     octokit,
+    selfRunId,
     sourceWorkflowId,
     sourceRunId,
     owner,
@@ -599,6 +731,8 @@ async function run(): Promise<void> {
     headBranch,
     sourceEventName,
     cancelMode,
+    notifyPRCancel,
+    notifyPRMessageStart,
     jobNameRegexps
   )
 


[airflow-cancel-workflow-runs] 44/44: Fix case of running cancel for different workflow

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 953e057dc81d3458935a18d1184c386b0f6b5738
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 21:00:39 2020 +0100

    Fix case of running cancel for different workflow
    
    During 4.3 the case with different workflow was broken.
    This is a fix to this problem.
---
 dist/index.js | 61 ++++++++++++++++++++++++++++++++++-------------------------
 src/main.ts   | 49 +++++++++++++++++++++++++++++++++++++----------
 2 files changed, 74 insertions(+), 36 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index e6422c4..4afd5db 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1491,10 +1491,11 @@ var CancelMode;
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
  * @param triggeringRunInfo the object identifying the triggering workflow
+ * @param sourceWorkflowId - workflow id to act on
  * @returns the unique string id for the group
  */
-function getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo) {
-    return (`:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+function getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo, sourceWorkflowId) {
+    return (`:${sourceWorkflowId}:${triggeringRunInfo.headRepo}` +
         `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`);
 }
 /**
@@ -1513,14 +1514,15 @@ function getCommonGroupIdFromRunItem(runItem) {
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
  * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param sourceWorkflowId - workflow id to act on
  * @return query parameters merged with the listWorkflowRuns criteria
  */
-function createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo) {
+function createListRunsQueryRunsSameSource(repositoryInfo, sourceWorkflowId, status, triggeringRunInfo) {
     const request = {
         owner: repositoryInfo.owner,
         repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: triggeringRunInfo.workflowId,
+        workflow_id: sourceWorkflowId,
         status,
         branch: triggeringRunInfo.headBranch,
         event: triggeringRunInfo.eventName
@@ -1530,16 +1532,17 @@ function createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRun
 /**
  * Creates query parameters selecting only specific run Id.
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param status - status of the run that we are querying for
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
-function createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo) {
+function createListRunsQuerySpecificRunId(repositoryInfo, sourceWorkflowId, status, triggeringRunInfo) {
     const request = {
         owner: repositoryInfo.owner,
         repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: triggeringRunInfo.workflowId,
+        workflow_id: sourceWorkflowId,
         status,
         // eslint-disable-next-line @typescript-eslint/camelcase
         run_id: triggeringRunInfo.runId.toString()
@@ -1747,10 +1750,11 @@ function getWorkflowRuns(repositoryInfo, statusValues, cancelMode, createListRun
  * @param runItem item to check
  * @param cancelFutureDuplicates whether future duplicates are being cancelled
  * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param sourceWorkflowId - workflow id to act on
  * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if we determine that the run Id should be cancelled
  */
-function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates) {
+function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, sourceWorkflowId, mapOfWorkflowRunCandidates) {
     const runHeadRepo = runItem.head_repository.full_name;
     if (triggeringRunInfo.headRepo !== undefined &&
         runHeadRepo !== triggeringRunInfo.headRepo) {
@@ -1759,7 +1763,7 @@ function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, t
     }
     if (cancelFutureDuplicates) {
         core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
-        addWorkflowRunToMap(getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+        addWorkflowRunToMap(getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo, sourceWorkflowId), runItem, mapOfWorkflowRunCandidates);
     }
     else {
         if (runItem.id === triggeringRunInfo.runId) {
@@ -1860,6 +1864,7 @@ function checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRe
  * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
  * of workflow names if it is.
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param runItem - run item
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode
@@ -1868,7 +1873,7 @@ function checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRe
  * @param skipEventTypes - which events should be skipped
  * @param mapOfWorkflowRunCandidates - map of workflow runs to add candidates to
  */
-function checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNamesRegexps, skipEventTypes, mapOfWorkflowRunCandidates) {
+function checkCandidateForCancelling(repositoryInfo, sourceWorkflowId, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNamesRegexps, skipEventTypes, mapOfWorkflowRunCandidates) {
     return __awaiter(this, void 0, void 0, function* () {
         if ('completed' === runItem.status.toString()) {
             core.info(`\nThe run ${runItem.id} is completed. Not adding as candidate to cancel.\n`);
@@ -1894,7 +1899,7 @@ function checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo,
             checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
-            checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates);
+            checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, sourceWorkflowId, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.ALL_DUPLICATES) {
             checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
@@ -1933,34 +1938,35 @@ function cancelRun(repositoryInfo, runId) {
 /**
  * Returns map of workflow run items matching the criteria specified group by workflow run id
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param statusValues - status values we want to check
  * @param cancelMode - cancel mode to use
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return map of the run items matching grouped by workflow run id
  */
-function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo) {
+function getWorkflowRunsMatchingCriteria(repositoryInfo, sourceWorkflowId, statusValues, cancelMode, triggeringRunInfo) {
     return __awaiter(this, void 0, void 0, function* () {
         return yield getWorkflowRuns(repositoryInfo, statusValues, cancelMode, function (status) {
             if (cancelMode === CancelMode.SELF) {
                 core.info(`\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Workflow ID:${triggeringRunInfo.workflowId},` +
+                    `Workflow ID:${sourceWorkflowId},` +
                     `Source Run id: ${triggeringRunInfo.runId}\n`);
-                return createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo);
+                return createListRunsQuerySpecificRunId(repositoryInfo, sourceWorkflowId, status, triggeringRunInfo);
             }
             else if (cancelMode === CancelMode.FAILED_JOBS ||
                 cancelMode === CancelMode.NAMED_JOBS ||
                 cancelMode === CancelMode.ALL_DUPLICATES ||
                 cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
                 core.info(`\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`);
-                return createListRunsQueryAllRuns(repositoryInfo, status, triggeringRunInfo.workflowId);
+                    `Status: ${status} Workflow ID:${sourceWorkflowId}\n`);
+                return createListRunsQueryAllRuns(repositoryInfo, status, sourceWorkflowId);
             }
             else if (cancelMode === CancelMode.DUPLICATES) {
                 core.info(`\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+                    `Status: ${status} Workflow ID:${sourceWorkflowId}, ` +
                     `Head Branch: ${triggeringRunInfo.headBranch},` +
                     `Event name: ${triggeringRunInfo.eventName}\n`);
-                return createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo);
+                return createListRunsQueryRunsSameSource(repositoryInfo, sourceWorkflowId, status, triggeringRunInfo);
             }
             else {
                 throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it.\n`);
@@ -2017,6 +2023,7 @@ function findPullRequestForRunItem(repositoryInfo, runItem) {
  * same group are put together in one array - in a map indexed by the source group id.
  *
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode to use
  * @param cancelFutureDuplicates - whether to cancel future duplicates
@@ -2029,7 +2036,7 @@ function findPullRequestForRunItem(repositoryInfo, runItem) {
  *       * source group id (allDuplicates mode)
  *       * matching job name (allDuplicatedMatchingJobNames mode)
  */
-function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns) {
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, sourceWorkflowId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns) {
     return __awaiter(this, void 0, void 0, function* () {
         const mapOfWorkflowRunCandidates = new Map();
         for (const [key, runItem] of workflowRuns) {
@@ -2039,7 +2046,7 @@ function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, can
                 core.info(`\nI have self-preservation built in. I refuse to cancel myself :)\n`);
                 continue;
             }
-            yield checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
+            yield checkCandidateForCancelling(repositoryInfo, sourceWorkflowId, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
         }
         return mapOfWorkflowRunCandidates;
     });
@@ -2150,6 +2157,7 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandida
  * Find and cancels runs based on the criteria chosen.
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - workflow id to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
@@ -2161,11 +2169,11 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandida
  * @param selfPreservation - whether the run will cancel itself if requested
  * @return array of canceled workflow run ids
  */
-function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation) {
+function findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
-        const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo);
-        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns);
+        const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, sourceWorkflowId, statusValues, cancelMode, triggeringRunInfo);
+        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, sourceWorkflowId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns);
         return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
     });
 }
@@ -2221,6 +2229,7 @@ function getTriggeringRunInfo(repositoryInfo, runId) {
  *
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - id of the workflow to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -2232,11 +2241,11 @@ function getTriggeringRunInfo(repositoryInfo, runId) {
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param selfPreservation - whether the run will cancel itself if requested
  */
-function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation) {
+function performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
-            `run id: ${triggeringRunInfo.runId}, ` +
+            `run id: ${triggeringRunInfo.runId}, Source workflow id: ${sourceWorkflowId}, ` +
             `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
             `sourceEventName: ${triggeringRunInfo.eventName}, ` +
             `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
@@ -2276,7 +2285,7 @@ function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMo
             throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation);
+        return yield findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation);
     });
 }
 /**
@@ -2445,7 +2454,7 @@ function run() {
         const triggeringRunInfo = yield getTriggeringRunInfo(repositoryInfo, sourceRunId);
         produceBasicOutputs(triggeringRunInfo);
         yield notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart);
-        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation);
+        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index cd99a35..109dd86 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -53,13 +53,15 @@ interface TriggeringRunInfo {
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
  * @param triggeringRunInfo the object identifying the triggering workflow
+ * @param sourceWorkflowId - workflow id to act on
  * @returns the unique string id for the group
  */
 function getCommonGroupIdFromTriggeringRunInfo(
-  triggeringRunInfo: TriggeringRunInfo
+  triggeringRunInfo: TriggeringRunInfo,
+  sourceWorkflowId: string | number
 ): string {
   return (
-    `:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+    `:${sourceWorkflowId}:${triggeringRunInfo.headRepo}` +
     `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`
   )
 }
@@ -85,10 +87,12 @@ function getCommonGroupIdFromRunItem(
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
  * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param sourceWorkflowId - workflow id to act on
  * @return query parameters merged with the listWorkflowRuns criteria
  */
 function createListRunsQueryRunsSameSource(
   repositoryInfo: RepositoryInfo,
+  sourceWorkflowId: string | number,
   status: string,
   triggeringRunInfo: TriggeringRunInfo
 ): rest.RequestOptions {
@@ -96,7 +100,7 @@ function createListRunsQueryRunsSameSource(
     owner: repositoryInfo.owner,
     repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    workflow_id: triggeringRunInfo.workflowId,
+    workflow_id: sourceWorkflowId,
     status,
     branch: triggeringRunInfo.headBranch,
     event: triggeringRunInfo.eventName
@@ -106,12 +110,14 @@ function createListRunsQueryRunsSameSource(
 /**
  * Creates query parameters selecting only specific run Id.
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param status - status of the run that we are querying for
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
 function createListRunsQuerySpecificRunId(
   repositoryInfo: RepositoryInfo,
+  sourceWorkflowId: string | number,
   status: string,
   triggeringRunInfo: TriggeringRunInfo
 ): rest.RequestOptions {
@@ -119,7 +125,7 @@ function createListRunsQuerySpecificRunId(
     owner: repositoryInfo.owner,
     repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    workflow_id: triggeringRunInfo.workflowId,
+    workflow_id: sourceWorkflowId,
     status,
     // eslint-disable-next-line @typescript-eslint/camelcase
     run_id: triggeringRunInfo.runId.toString()
@@ -353,6 +359,7 @@ async function getWorkflowRuns(
  * @param runItem item to check
  * @param cancelFutureDuplicates whether future duplicates are being cancelled
  * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param sourceWorkflowId - workflow id to act on
  * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if we determine that the run Id should be cancelled
  */
@@ -360,6 +367,7 @@ function checkCandidateForCancellingDuplicate(
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
   cancelFutureDuplicates: boolean,
   triggeringRunInfo: TriggeringRunInfo,
+  sourceWorkflowId: string | number,
   mapOfWorkflowRunCandidates: Map<
     string,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
@@ -380,7 +388,10 @@ function checkCandidateForCancellingDuplicate(
       `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
     )
     addWorkflowRunToMap(
-      getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo),
+      getCommonGroupIdFromTriggeringRunInfo(
+        triggeringRunInfo,
+        sourceWorkflowId
+      ),
       runItem,
       mapOfWorkflowRunCandidates
     )
@@ -551,6 +562,7 @@ async function checkCandidateForDuplicateNamedJobs(
  * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
  * of workflow names if it is.
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param runItem - run item
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode
@@ -561,6 +573,7 @@ async function checkCandidateForDuplicateNamedJobs(
  */
 async function checkCandidateForCancelling(
   repositoryInfo: RepositoryInfo,
+  sourceWorkflowId: string | number,
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
   triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
@@ -619,6 +632,7 @@ async function checkCandidateForCancelling(
       runItem,
       cancelFutureDuplicates,
       triggeringRunInfo,
+      sourceWorkflowId,
       mapOfWorkflowRunCandidates
     )
   } else if (cancelMode === CancelMode.ALL_DUPLICATES) {
@@ -668,6 +682,7 @@ async function cancelRun(
 /**
  * Returns map of workflow run items matching the criteria specified group by workflow run id
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param statusValues - status values we want to check
  * @param cancelMode - cancel mode to use
  * @param triggeringRunInfo - information about the workflow that triggered the run
@@ -675,6 +690,7 @@ async function cancelRun(
  */
 async function getWorkflowRunsMatchingCriteria(
   repositoryInfo: RepositoryInfo,
+  sourceWorkflowId: string | number,
   statusValues: string[],
   cancelMode: CancelMode,
   triggeringRunInfo: TriggeringRunInfo
@@ -687,11 +703,12 @@ async function getWorkflowRunsMatchingCriteria(
       if (cancelMode === CancelMode.SELF) {
         core.info(
           `\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Workflow ID:${triggeringRunInfo.workflowId},` +
+            `Workflow ID:${sourceWorkflowId},` +
             `Source Run id: ${triggeringRunInfo.runId}\n`
         )
         return createListRunsQuerySpecificRunId(
           repositoryInfo,
+          sourceWorkflowId,
           status,
           triggeringRunInfo
         )
@@ -703,22 +720,23 @@ async function getWorkflowRunsMatchingCriteria(
       ) {
         core.info(
           `\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`
+            `Status: ${status} Workflow ID:${sourceWorkflowId}\n`
         )
         return createListRunsQueryAllRuns(
           repositoryInfo,
           status,
-          triggeringRunInfo.workflowId
+          sourceWorkflowId
         )
       } else if (cancelMode === CancelMode.DUPLICATES) {
         core.info(
           `\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+            `Status: ${status} Workflow ID:${sourceWorkflowId}, ` +
             `Head Branch: ${triggeringRunInfo.headBranch},` +
             `Event name: ${triggeringRunInfo.eventName}\n`
         )
         return createListRunsQueryRunsSameSource(
           repositoryInfo,
+          sourceWorkflowId,
           status,
           triggeringRunInfo
         )
@@ -793,6 +811,7 @@ async function findPullRequestForRunItem(
  * same group are put together in one array - in a map indexed by the source group id.
  *
  * @param repositoryInfo - information about the repository used
+ * @param sourceWorkflowId - workflow id to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode to use
  * @param cancelFutureDuplicates - whether to cancel future duplicates
@@ -807,6 +826,7 @@ async function findPullRequestForRunItem(
  */
 async function filterAndMapWorkflowRunsToGroups(
   repositoryInfo: RepositoryInfo,
+  sourceWorkflowId: string | number,
   triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
@@ -835,6 +855,7 @@ async function filterAndMapWorkflowRunsToGroups(
     }
     await checkCandidateForCancelling(
       repositoryInfo,
+      sourceWorkflowId,
       runItem,
       triggeringRunInfo,
       cancelMode,
@@ -1009,6 +1030,7 @@ async function cancelTheRunsPerGroup(
  * Find and cancels runs based on the criteria chosen.
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - workflow id to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
@@ -1023,6 +1045,7 @@ async function cancelTheRunsPerGroup(
 async function findAndCancelRuns(
   repositoryInfo: RepositoryInfo,
   selfRunId: number,
+  sourceWorkflowId: string | number,
   triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
@@ -1036,12 +1059,14 @@ async function findAndCancelRuns(
   const statusValues = ['queued', 'in_progress']
   const workflowRuns = await getWorkflowRunsMatchingCriteria(
     repositoryInfo,
+    sourceWorkflowId,
     statusValues,
     cancelMode,
     triggeringRunInfo
   )
   const mapOfWorkflowRunCandidatesCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
     repositoryInfo,
+    sourceWorkflowId,
     triggeringRunInfo,
     cancelMode,
     cancelFutureDuplicates,
@@ -1125,6 +1150,7 @@ async function getTriggeringRunInfo(
  *
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - id of the workflow to act on
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -1139,6 +1165,7 @@ async function getTriggeringRunInfo(
 async function performCancelJob(
   repositoryInfo: RepositoryInfo,
   selfRunId: number,
+  sourceWorkflowId: string | number,
   triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   notifyPRCancel: boolean,
@@ -1154,7 +1181,7 @@ async function performCancelJob(
   )
   core.info(
     `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
-      `run id: ${triggeringRunInfo.runId}, ` +
+      `run id: ${triggeringRunInfo.runId}, Source workflow id: ${sourceWorkflowId}, ` +
       `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
       `sourceEventName: ${triggeringRunInfo.eventName}, ` +
       `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
@@ -1209,6 +1236,7 @@ async function performCancelJob(
   return await findAndCancelRuns(
     repositoryInfo,
     selfRunId,
+    sourceWorkflowId,
     triggeringRunInfo,
     cancelMode,
     cancelFutureDuplicates,
@@ -1469,6 +1497,7 @@ async function run(): Promise<void> {
   const cancelledRuns = await performCancelJob(
     repositoryInfo,
     selfRunId,
+    sourceWorkflowId,
     triggeringRunInfo,
     cancelMode,
     notifyPRCancel,


[airflow-cancel-workflow-runs] 29/44: Fix example

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 7dc574153737e89e25bbdf05a28ea013ba00b6a0
Author: Tobiasz Kędzierski <to...@gmail.com>
AuthorDate: Sat Aug 29 12:16:37 2020 +0200

    Fix example
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index f35d466..021e34f 100644
--- a/README.md
+++ b/README.md
@@ -520,7 +520,7 @@ jobs:
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
       - name: "Extract canceled failed runs"
         id: extract-cancelled-failed-runs
-        if: steps.cancel-failed.outputs.cancelledRuns != "[]"
+        if: steps.cancel-failed.outputs.cancelledRuns != '[]'
         run: |
             REGEXP="Fail fast CI. Source run: "
             SEPARATOR=""
@@ -531,7 +531,7 @@ jobs:
             done
             echo "::set-output name=matching-regexp::${REGEXP}"
       - name: "Cancel triggered 'Cancelling' runs for the cancelled failed runs"
-        if: steps.cancel-failed.outputs.cancelledRuns != "[]"
+        if: steps.cancel-failed.outputs.cancelledRuns != '[]'
         uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: namedJobs


[airflow-cancel-workflow-runs] 15/44: Update README.md

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 35d676655f12a395cc2e49ba2f1fb24ac7a65c7f
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Wed Feb 19 14:59:37 2020 -0600

    Update README.md
---
 README.md | 4 +++-
 1 file changed, 3 insertions(+), 1 deletion(-)

diff --git a/README.md b/README.md
index c357988..056b04c 100644
--- a/README.md
+++ b/README.md
@@ -23,7 +23,9 @@ name: Cleanup Duplicate Branches and PRs
 on:
   schedule:
     - cron:  '*/15 * * * *'
-cancel-runs: 
+cancel-runs:
+  # Prevent forks from running this to be nice
+  if: github.repository == 'foo-org/my-repo'
   runs-on: ubuntu-latest
     steps:
       - uses: n1hility/cancel-previous-runs@v2


[airflow-cancel-workflow-runs] 22/44: Merge pull request #2 from n1hility/dependabot/npm_and_yarn/acorn-5.7.4

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 7d3eeffe6c69cdc0932784e808171dbaaf23794a
Merge: 0180a5b cc3b6f2
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Tue May 5 23:11:30 2020 -0500

    Merge pull request #2 from n1hility/dependabot/npm_and_yarn/acorn-5.7.4
    
    Bump acorn from 5.7.3 to 5.7.4

 package-lock.json | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)


[airflow-cancel-workflow-runs] 08/44: release

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 28fd9a740a7a34a3281f8130e85e6a7cb1bfa66c
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Thu Feb 6 13:27:47 2020 -0600

    release
---
 dist/index.js | 24 ++++++++++++++----------
 1 file changed, 14 insertions(+), 10 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 1d02837..a5661b6 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1476,12 +1476,13 @@ function run() {
             const [owner, repo] = repository.split('/');
             const branchPrefix = 'refs/heads/';
             const tagPrefix = 'refs/tags/';
-            if (eventName !== 'push') {
-                core.info('Skipping non-push event');
+            if (!['push', 'pull_request'].includes(eventName)) {
+                core.info('Skipping unsupported event');
                 return;
             }
-            let branch = getRequiredEnv('GITHUB_REF');
-            if (!branch.startsWith(branchPrefix)) {
+            const pullRequest = 'pull_request' === eventName;
+            let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF');
+            if (!pullRequest && !branch.startsWith(branchPrefix)) {
                 if (branch.startsWith(tagPrefix)) {
                     core.info(`Skipping tag build`);
                     return;
@@ -1496,31 +1497,34 @@ function run() {
                 owner,
                 repo,
                 branch,
-                event: 'push'
+                event: pullRequest ? 'pull_request' : 'push'
             });
             let matched = false;
             let workflow = '';
-            let count = 0;
+            let headRepoName = '';
             try {
                 for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
                     const item = _c.value;
                     // There is some sort of bug where the pagination URLs point to a
-                    // different URL with a different data format
-                    const elements = ++count < 2 ? item.data : item.data.workflow_runs;
+                    // different endpoint URL which trips up the resulting representation
+                    // In that case, fallback to the actual REST 'workflow_runs' property
+                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
                     for (const element of elements) {
                         core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
                         if (!matched) {
                             if (element.id.toString() === selfRunId) {
                                 matched = true;
                                 workflow = element.workflow_url;
+                                headRepoName = pullRequest ? element.head_repository.full_name : '';
                             }
                             // Skip everything up to and matching this run
                             continue;
                         }
                         // Only cancel jobs with the same workflow
                         if (workflow === element.workflow_url &&
-                            element.status.toString() !== 'completed') {
-                            Promise.resolve(cancelRun(octokit, owner, repo, element.id));
+                            element.status.toString() !== 'completed' &&
+                            (!pullRequest || headRepoName === element.head_repository.full_name)) {
+                            yield cancelRun(octokit, owner, repo, element.id);
                         }
                     }
                 }


[airflow-cancel-workflow-runs] 10/44: optimize queries to only pull active statuses

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit a38415f9b1bd6df21448f98b429f51cc2f3c412f
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 18 22:48:39 2020 -0600

    optimize queries to only pull active statuses
---
 __tests__/main.test.ts |    8 +-
 dist/index.js          | 3951 +++++++++++++++++++++++++++++++++++++++++++++++-
 package-lock.json      |    5 +
 package.json           |    3 +-
 src/main.ts            |  137 +-
 5 files changed, 3996 insertions(+), 108 deletions(-)

diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index 819d5d4..ab5dc2b 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -8,13 +8,13 @@ test('no op', () => {})
 // test('test runs', () => {
 //   const ip = path.join(__dirname, '..', 'lib', 'main.js')
 //   process.env['INPUT_TOKEN'] = ''
-//   //process.env['INPUT_WORKFLOW'] = 'ci-actions.yml'
+//   process.env['INPUT_WORKFLOW'] = 'ci-actions.yml'
 //   process.env['GITHUB_RUN_ID'] = '41374869' //'33782469'
 //   process.env['GITHUB_REPOSITORY'] = ''
 //   //process.env['GITHUB_HEAD_REF'] = 'refs/heads/n1hility-patch-5'
-//   process.env['GITHUB_REF'] = 'refs/heads/master'
-//   process.env['GITHUB_EVENT_NAME'] = 'push'
-//   // process.env['GITHUB_EVENT_NAME'] = 'schedule'
+//   //process.env['GITHUB_REF'] = 'refs/heads/master'
+//   // process.env['GITHUB_EVENT_NAME'] = 'push'
+//   process.env['GITHUB_EVENT_NAME'] = 'schedule'
 
 //   //   process.env['GITHUB_RUN_ID'] = '35599067'
 //   //   process.env['GITHUB_REPOSITORY'] = ''
diff --git a/dist/index.js b/dist/index.js
index 16c7215..ac890d9 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1465,12 +1465,33 @@ var __importStar = (this && this.__importStar) || function (mod) {
 Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
+const treemap = __importStar(__webpack_require__(706));
+function createRunsQuery(octokit, owner, repo, workflowId, status, branch, event) {
+    const request = branch === undefined
+        ? {
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            workflow_id: workflowId,
+            status
+        }
+        : {
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            workflow_id: workflowId,
+            status,
+            branch,
+            event
+        };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
 function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, event) {
     var e_1, _a;
     return __awaiter(this, void 0, void 0, function* () {
         const octokit = new github.GitHub(token);
         // Deteermind the workflow to reduce the result set, or reference anothre workflow
-        let resolvedId;
+        let resolvedId = '';
         if (workflowId === undefined) {
             const reply = yield octokit.actions.getWorkflowRun({
                 owner,
@@ -1478,69 +1499,65 @@ function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, eve
                 // eslint-disable-next-line @typescript-eslint/camelcase
                 run_id: Number.parseInt(selfRunId)
             });
-            resolvedId = reply.data.workflow_url.split('/').pop();
+            resolvedId = reply.data.workflow_url.split('/').pop() || '';
+            if (!(resolvedId.length > 0)) {
+                throw new Error('Could not resolve workflow');
+            }
         }
         else {
             resolvedId = workflowId;
         }
         core.info(`Workflow ID is: ${resolvedId}`);
-        const request = branch === undefined
-            ? {
-                owner,
-                repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                workflow_id: resolvedId
+        // eslint-disable-next-line @typescript-eslint/no-explicit-any
+        const sorted = new treemap.TreeMap();
+        for (const status of ['queued', 'in_progress']) {
+            const listRuns = createRunsQuery(octokit, owner, repo, resolvedId, status, branch, event);
+            try {
+                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                    const item = _c.value;
+                    // There is some sort of bug where the pagination URLs point to a
+                    // different endpoint URL which trips up the resulting representation
+                    // In that case, fallback to the actual REST 'workflow_runs' property
+                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
+                    for (const element of elements) {
+                        sorted.set(element.run_number, element);
+                    }
+                }
             }
-            : {
-                owner,
-                repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                workflow_id: resolvedId,
-                branch,
-                event
-            };
-        const listRuns = octokit.actions.listWorkflowRuns.endpoint.merge(request);
+            catch (e_1_1) { e_1 = { error: e_1_1 }; }
+            finally {
+                try {
+                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+                }
+                finally { if (e_1) throw e_1.error; }
+            }
+        }
         // If a workflow was provided process everything
         let matched = workflowId !== undefined;
         const heads = new Set();
-        try {
-            for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
-                const item = _c.value;
-                // There is some sort of bug where the pagination URLs point to a
-                // different endpoint URL which trips up the resulting representation
-                // In that case, fallback to the actual REST 'workflow_runs' property
-                const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
-                for (const element of elements) {
-                    core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
-                    if (!matched) {
-                        if (element.id.toString() !== selfRunId) {
-                            // Skip everything up to this run
-                            continue;
-                        }
-                        matched = true;
-                        core.info(`Matched ${selfRunId}`);
-                    }
-                    if ('completed' === element.status.toString()) {
-                        continue;
-                    }
-                    // This is a set of one in the non-schedule case, otherwise everything is a candidate
-                    const head = `${element.head_repository.full_name}/${element.head_branch}`;
-                    if (!heads.has(head)) {
-                        core.info(`First: ${head}`);
-                        heads.add(head);
-                        continue;
-                    }
-                    core.info(`Cancelling: ${head}`);
-                    yield cancelRun(octokit, owner, repo, element.id);
+        for (const entry of sorted.backward()) {
+            const element = entry[1];
+            core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
+            if (!matched) {
+                if (element.id.toString() !== selfRunId) {
+                    // Skip everything up to this run
+                    continue;
                 }
+                matched = true;
+                core.info(`Matched ${selfRunId}`);
             }
-        }
-        catch (e_1_1) { e_1 = { error: e_1_1 }; }
-        finally {
-            try {
-                if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+            if ('completed' === element.status.toString()) {
+                continue;
+            }
+            // This is a set of one in the non-schedule case, otherwise everything is a candidate
+            const head = `${element.head_repository.full_name}/${element.head_branch}`;
+            if (!heads.has(head)) {
+                core.info(`First: ${head}`);
+                heads.add(head);
+                continue;
             }
-            finally { if (e_1) throw e_1.error; }
+            core.info(`Cancelling: ${head}`);
+            yield cancelRun(octokit, owner, repo, element.id);
         }
     });
 }
@@ -8711,6 +8728,3836 @@ module.exports = (promise, onFinally) => {
 
 /***/ }),
 
+/***/ 706:
+/***/ (function(module) {
+
+(function webpackUniversalModuleDefinition(root, factory) {
+	if(true)
+		module.exports = factory();
+	else { var i, a; }
+})(typeof self !== 'undefined' ? self : this, function() {
+return /******/ (function(modules) { // webpackBootstrap
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	// expose the modules object (__webpack_modules__)
+/******/ 	__webpack_require__.m = modules;
+/******/
+/******/ 	// expose the module cache
+/******/ 	__webpack_require__.c = installedModules;
+/******/
+/******/ 	// define getter function for harmony exports
+/******/ 	__webpack_require__.d = function(exports, name, getter) {
+/******/ 		if(!__webpack_require__.o(exports, name)) {
+/******/ 			Object.defineProperty(exports, name, {
+/******/ 				configurable: false,
+/******/ 				enumerable: true,
+/******/ 				get: getter
+/******/ 			});
+/******/ 		}
+/******/ 	};
+/******/
+/******/ 	// getDefaultExport function for compatibility with non-harmony modules
+/******/ 	__webpack_require__.n = function(module) {
+/******/ 		var getter = module && module.__esModule ?
+/******/ 			function getDefault() { return module['default']; } :
+/******/ 			function getModuleExports() { return module; };
+/******/ 		__webpack_require__.d(getter, 'a', getter);
+/******/ 		return getter;
+/******/ 	};
+/******/
+/******/ 	// Object.prototype.hasOwnProperty.call
+/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
+/******/
+/******/ 	// __webpack_public_path__
+/******/ 	__webpack_require__.p = "";
+/******/
+/******/ 	// Load entry module and return exports
+/******/ 	return __webpack_require__(__webpack_require__.s = 5);
+/******/ })
+/************************************************************************/
+/******/ ([
+/* 0 */
+/***/ (function(module, exports, __nested_webpack_require_2850__) {
+
+"use strict";
+
+
+/**
+ * @private
+ */
+const RED = 1;
+/**
+ * @private
+ */
+const BLACK = 2;
+
+/**
+ * @private
+ * A node for a red-black tree
+ */
+class TreeNode {
+
+    /**
+     * Default constructor
+     */
+    constructor() {
+        /** left child */
+        this.left = null;
+        /** right child */
+        this.right = null;
+        /** parent node */
+        this.parent = null;
+        /** key object (additional 'value' data member is added in map-like classes) */
+        this.key = null;
+        /** by default new node is red */
+        this.color = RED;
+    }
+
+    /**
+     * @returns parent of parent
+     */
+    grandparent() {
+        let p = this.parent;
+        if (p === null) {
+            return null;
+        } // No parent means no grandparent
+        return p.parent;
+    }
+
+    /**
+     * @returns the other child of the same parent
+     */
+    sibling() {
+        let p = this.parent;
+        if (p === null) {
+            return null;
+        } // No parent means no sibling
+        if (this === p.left) {
+            return p.right;
+        }
+        else {
+            return p.left;
+        }
+    }
+
+    /**
+     * @returns another child of the grandparent
+     */
+    uncle() {
+        let p = this.parent;
+        if (p === null) {
+            return null;
+        } // No parent means no uncle
+        let g = p.parent;
+        if (g === null) {
+            return null;
+        } // No grandparent means no uncle
+        return p.sibling();
+    }
+}
+
+module.exports = {
+    TreeNode: TreeNode,
+    BLACK: BLACK,
+    RED: RED
+};
+
+/***/ }),
+/* 1 */
+/***/ (function(module, exports) {
+
+/**
+ * Used by sets
+ * @private
+ */
+class KeyOnlyPolicy {
+    /**
+     * Returns key data from the specified node
+     * @param {*} n
+     */
+    fetch(n) {
+        return n.key;
+    }
+
+    /**
+     * Copies key data from one node to another
+     * @param {*} dst
+     * @param {*} src
+     */
+    copy(dst, src) {
+        dst.key = src.key;
+    }
+
+    /**
+     * @returns string representation of the key
+     * @param {*} node
+     */
+    toString(node) {
+        return String(node.key);
+    }
+}
+
+/**
+ * Used by maps
+ * @private
+ */
+class KeyValuePolicy {
+    /**
+     * Returns key-value data from the specified node
+     * @param {*} n
+     */
+    fetch(n) {
+        return [n.key, n.value];
+    }
+
+    /**
+     * Copies key-value data from one node to another
+     * @param {*} dst
+     * @param {*} src
+     */
+    copy(dst, src) {
+        dst.key = src.key;
+        dst.value = src.value;
+    }
+
+    /**
+     * @returns string representation of key-value pair
+     * @param {*} node
+     */
+    toString(node) {
+        return String(node.key) + ':' + String(node.value);
+    }
+}
+
+/**
+ * Used for iteration through values of a map
+ * @private
+ */
+class ValueOnlyPolicy {
+    /**
+     * Returns data from the specified node
+     * @param {*} n
+     */
+    fetch(n) {
+        return n.value;
+    }
+
+    /**
+     * Copies value data from one node to another
+     * @param {*} dst
+     * @param {*} src
+     */
+    copy(dst, src) {
+        dst.value = src.value;
+    }
+
+    /**
+     * @returns string representation of node's value
+     * @param {*} node
+     */
+    toString(node) {
+        return String(node.value);
+    }
+}
+
+module.exports = {
+    KeyOnlyPolicy: KeyOnlyPolicy,
+    ValueOnlyPolicy: ValueOnlyPolicy,
+    KeyValuePolicy: KeyValuePolicy
+};
+
+/***/ }),
+/* 2 */
+/***/ (function(module, exports, __nested_webpack_require_6477__) {
+
+"use strict";
+
+
+/** @ignore */
+const {TreeNode, RED, BLACK} = __nested_webpack_require_6477__(0);
+/** @ignore */
+const {JsIterator, JsReverseIterator} = __nested_webpack_require_6477__(3);
+/** @ignore */
+const {Iterator, ReverseIterator} = __nested_webpack_require_6477__(4);
+/** @ignore */
+const {KeyOnlyPolicy, ValueOnlyPolicy, KeyValuePolicy} = __nested_webpack_require_6477__(1);
+/** @ignore */
+const {InsertionResult} = __nested_webpack_require_6477__(8);
+
+/** insertion mode of a multimap, nodes with the same keys can be added */
+const INSERT_MULTI = 1;
+/** if a node with the same key already exists then the subsequent attempts are ignored */
+const INSERT_UNIQUE = 2;
+/** if a node with the same key already exists then it's value is replaced on subsequent attempts */
+const INSERT_REPLACE = 3;
+
+/**
+ * @private
+ * Special node in a tree is created for performance reasons
+ */
+class Head {
+    /** default constructor */
+    constructor() {
+        /** node with the smallest key */
+        this.leftmost = this;
+        /** node with the largest key */
+        this.rightmost = this;
+        /** root node of the tree */
+        this.root = this;
+        /** number of nodes in the tree */
+        this.size = 0;
+        /** extra tag used in debuggin of unit tests */
+        this.id = 'HEAD';
+    }
+}
+
+/**
+ * @private
+ * 3-way comparison, similar to strcmp and memcp in C programming language
+ * @returns +1 if the value of rhs is greater than lhs
+ *          -1 if the value of rhs is less than lhs
+ *           0 if values are the same
+ */
+function compare(lhs, rhs) {
+    if (lhs < rhs) {
+        return -1;
+    }
+    else if (lhs === rhs) {
+        return 0;
+    }
+    else {
+        return 1;
+    }
+}
+
+/**
+ * Red-black tree
+ * @access private
+ */
+class Tree {
+    /** default constructor of an empty tree */
+    constructor() {
+        /** head */
+        this.head = new Head();
+        /** 3-way comparison function */
+        this.compare = compare;
+        /** must be an instance of KeyOnlyPolicy for sets, or KeyValuePolicy for maps */
+        this.valuePolicy = new KeyOnlyPolicy();
+    }
+
+    /**
+     * Deletes all nodes in the tree
+     */
+    clear() {
+        this.head = new Head();
+    }
+
+    /**
+     * @returns number of nodes in the tree
+     */
+    size() {
+        return this.head.size;
+    }
+
+    /**
+     * @private
+     * A wrapper that calls 3-way comparison of node keys
+     * @param {*} lhs
+     * @param {*} rhs
+     */
+    compareNodes(lhs, rhs) {
+        return this.compare(lhs.key, rhs.key);
+    }
+
+    /**
+     * @private
+     * used by rotation operations
+     */
+    replaceNode(oldNode, newNode) {
+        if (oldNode === newNode) {
+            return;
+        }
+        if (oldNode.parent === null) {
+            this.head.root = newNode;
+        }
+        else {
+            if (oldNode === oldNode.parent.left) {
+                oldNode.parent.left = newNode;
+            }
+            else {
+                oldNode.parent.right = newNode;
+            }
+        }
+
+        if (!this.isLeaf(newNode)) {
+            newNode.parent = oldNode.parent;
+        }
+    }
+
+    /**
+     * Rebalances tree as described below
+
+              X                                           Y
+             / \                                         / \
+            Y   c         right rotate -->              a   X
+           / \            <--  left rotate                 / \
+          a   b                                           b   c
+     * @private
+     */
+    rotateLeft(node) {
+        let right = node.right;
+        if (this.isLeaf(right)) {
+            throw new Error('rotateLeft can\'t be performed. The tree is corrupted');
+        }
+        this.replaceNode(node, right);
+
+        node.right = right.left;
+        if (right.left !== null) {
+            right.left.parent = node;
+        }
+
+        right.left = node;
+        node.parent = right;
+    }
+
+    /**
+     * Rebalances tree as described in rotateLeft
+     * @param {*} node - parent node
+     */
+    rotateRight(node) {
+        let left = node.left;
+        if (this.isLeaf(left)) {
+            throw new Error('rotateRight can\'t be performed. The tree is corrupted');
+        }
+        this.replaceNode(node, left);
+
+        node.left = left.right;
+        if (left.right !== null) {
+            left.right.parent = node;
+        }
+
+        left.right = node;
+        node.parent = left;
+    }
+
+    /**
+     * @returns true - for null pointers and head node; false - for all other nodes
+     * @param {*} node
+     */
+    isLeaf(node) {
+        if (node === null || node === this.head) {
+            return true;
+        }
+        return false;
+    }
+
+    /**
+     * Leaf nodes are considered 'black'. All real nodes contain 'color' data member
+     * @param {*} node
+     */
+    fetchColor(node) {
+        if (this.isLeaf(node)) {
+            return BLACK;
+        }
+        else {
+            return node.color;
+        }
+    }
+
+    /**
+     * Tests a node for 'blackness'.
+     * @param {*} node
+     */
+    isBlack(node) {
+        return (this.fetchColor(node) === BLACK);
+    }
+
+    /**
+     * Tests node for 'redness'.
+     * @param {*} node
+     */
+    isRed(node) {
+        return (this.fetchColor(node) === RED);
+    }
+
+    /* ===========================
+       INSERT
+       =========================== */
+    /**
+     * A node will be inserted into the tree even if nodes with the same key already exist
+     * @param {*} node
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     */
+    insertMulti(node) {
+        return this.insertNode(node, INSERT_MULTI);
+    }
+
+    /**
+     * The node is inserted into the tree only if nodes with the same key do not exist there
+     * @param {*} node
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     */
+    insertUnique(node) {
+        return this.insertNode(node, INSERT_UNIQUE);
+    }
+
+    /**
+     * The node is inserted. If a node with the same key exists it's value will be replaced by the value of the new node
+     * @param {*} node
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     */
+    insertOrReplace(node) {
+        return this.insertNode(node, INSERT_REPLACE);
+    }
+
+    /**
+     * @private
+     * Inserts node. Updates head node. Rebalances tree.
+     * @param {*} n - node
+     * @param {*} mode - one of INSERT_MULTI, INSERT_UNIQUE, INSERT_REPLACE
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     */
+    insertNode(n, mode = INSERT_MULTI) {
+        let res = this.insertNodeInternal(this.head.root, n, mode);
+        if (res.wasAdded) {
+            if (this.head.size === 0) {
+                this.head.root = n;
+                this.head.leftmost = n;
+                this.head.rightmost = n;
+
+                n.left = this.head;
+                n.right = this.head;
+            }
+            else if (this.head.leftmost.left === n) {
+                this.head.leftmost = n;
+                n.left = this.head;
+            }
+            else if (this.head.rightmost.right === n) {
+                this.head.rightmost = n;
+                n.right = this.head;
+            }
+            this.insertRepairTree(n);
+            this.head.size = this.head.size + 1;
+        }
+        return res;
+    }
+
+    /**
+     * @private
+     * Inserts node according to the mode
+     * @param {*} root - root node of the tree
+     * @param {*} n - node to be inserted
+     * @param {*} mode - one of INSERT_MULTI, INSERT_UNIQUE, INSERT_REPLACE
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     */
+    insertNodeInternal(root, n, mode) {
+        // recursively descend the tree until a leaf is found
+        let x = root;
+        let y = null;
+        let rc = -1;
+        // find matching node
+        while (!this.isLeaf(x)) {
+            y = x;
+            rc = this.compareNodes(n, y);
+            if (rc < 0) {
+                x = y.left;
+            }
+            else if (rc > 0) {
+                x = y.right;
+            }
+            else {
+                // node with the same key value
+                switch (mode) {
+                    case INSERT_UNIQUE:
+                        // it's a duplicate
+                        return new InsertionResult(false, false, undefined);
+                    case INSERT_REPLACE:
+                        this.valuePolicy.copy(y, n);
+                        return new InsertionResult(false, true, new Iterator(y, this));
+                    default:
+                        // INSERT_MULTI
+                        x = y.right;
+                }
+            }
+        }
+        if (this.isLeaf(y)) {
+            n.parent = null;
+            n.left = this.head;
+            n.right = this.head;
+        }
+        else {
+            n.parent = y;
+            if (rc < 0) {
+                y.left = n;
+            }
+            else {
+                y.right = n;
+            }
+        }
+        return new InsertionResult(true, false, new Iterator(n, this));
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
+     * @param {*} n - node
+     */
+    insertRepairTree(n) {
+        if (n.parent === null) {
+            this.repairCase1(n);
+        }
+        else if (this.isBlack(n.parent)) {
+        /* insert_case2(n);
+           // do nothing */
+        }
+        else if (this.isRed(n.uncle())) {
+            this.repairCase3(n);
+        }
+        else {
+            this.repairCase4(n);
+        }
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
+     * @param {*} n - node
+     */
+    repairCase1(n) {
+        n.color = BLACK;
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
+     * @param {*} n - node
+     */
+    repairCase3(n) {
+        n.parent.color = BLACK;
+        n.uncle().color = BLACK;
+        n.grandparent().color = RED;
+        this.insertRepairTree(n.grandparent());
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
+     * @param {*} n - node
+     */
+    repairCase4(n) {
+        let p = n.parent;
+        let g = n.grandparent();
+
+        let nr = null;
+        if ((g.left !== null)
+            && (n === g.left.right)) {
+            this.rotateLeft(p);
+            n = n.left;
+        }
+        else if ((g.right !== null)
+            && (n === g.right.left)) {
+            this.rotateRight(p);
+            n = n.right;
+        }
+
+        p = n.parent;
+        g = n.grandparent();
+        if (n === p.left) {
+            this.rotateRight(g);
+        }
+        else {
+            this.rotateLeft(g);
+        }
+
+        p.color = BLACK;
+        g.color = RED;
+    }
+
+    /**
+     * @returns the node with the highest key for the subtree of the specified root node
+     * @param {*} node - root node of the subtree to be evaluated
+     */
+    fetchMaximum(node) {
+        while (!this.isLeaf(node.right)) {
+            node = node.right;
+        }
+
+        return node;
+    }
+
+    /**
+     * @returns the node with the lowest key for the subtree of the specified root node
+     * @param {*} node - root node of the subtree to be evaluated
+     */
+    fetchMinimum(node) {
+        while (!this.isLeaf(node.left)) {
+            node = node.left;
+        }
+
+        return node;
+    }
+
+    /* ===========================
+       ERASE
+       =========================== */
+    /**
+     * Removes node from the tree
+     * @param {*} node
+     */
+    erase(node) {
+        if (this.isLeaf(node)) {
+            return;
+        }
+
+        this.eraseInternal(node);
+        let h = this.head;
+        h.size = h.size - 1;
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node - node
+     */
+    eraseInternal(node) {
+        if (!this.isLeaf(node.left)
+            && !this.isLeaf(node.right)) {
+            let pred = this.fetchMaximum(node.left);
+
+            this.valuePolicy.copy(node, pred);
+            node = pred;
+        }
+
+        let child = (this.isLeaf(node.right)) ? node.left : node.right;
+
+        if (this.isBlack(node)) {
+            this.eraseCase1(node);
+        }
+        this.replaceNode(node, child);
+        if (this.head.size === 2) {
+            if (!this.isLeaf(child)) {
+                // Root node must be BLACK
+                child.color = BLACK;
+            }
+        }
+
+        let h = this.head;
+        if (this.isLeaf(child)) {
+            /* The node didn't have children and it was removed
+               the head needs to update leftmost, rightmost pointers */
+            if (h.leftmost === node) {
+                let p = node.parent;
+                if (p !== null) {
+                    h.leftmost = p;
+                    p.left = h;
+                }
+                else {
+                    h.leftmost = h;
+                }
+            }
+            if (h.rightmost === node) {
+                let p = node.parent;
+                if (p !== null) {
+                    h.rightmost = p;
+                    p.right = h;
+                }
+                else {
+                    h.rightmost = h;
+                }
+            }
+        }
+        else {
+            // the node had a child. Now node is removed. Any references should point to the child now
+            if (h.leftmost === node) {
+                h.leftmost = child;
+                child.left = h;
+            }
+            if (h.rightmost === node) {
+                h.rightmost = child;
+                child.right = h;
+            }
+        }
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase1(node) {
+        if (node.parent === null) {
+            return;
+        }
+        else {
+            this.eraseCase2(node);
+        }
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase2(node) {
+        let s = node.sibling();
+
+        if (this.isRed(s)) {
+            node.parent.color = RED;
+            s.color = BLACK;
+
+            if (node === node.parent.left) {
+                this.rotateLeft(node.parent);
+            }
+            else {
+                this.rotateRight(node.parent);
+            }
+        }
+        this.eraseCase3(node);
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase3(node) {
+        let s = node.sibling();
+        let p = node.parent;
+        if (this.isBlack(p)
+            && this.isBlack(s)
+            && this.isBlack(s.left)
+            && this.isBlack(s.right)) {
+
+            s.color = RED;
+            this.eraseCase1(p);
+        }
+        else {
+            this.eraseCase4(node);
+        }
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase4(node) {
+        let s = node.sibling();
+        let p = node.parent;
+        if (this.isRed(p)
+            && this.isBlack(s)
+            && this.isBlack(s.left)
+            && this.isBlack(s.right)) {
+
+            s.color = RED;
+            p.color = BLACK;
+        }
+        else {
+            this.eraseCase5(node);
+        }
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase5(node) {
+        let s = node.sibling();
+        let p = node.parent;
+        /* The check below is unnecessary
+           due to case 2 (even though case 2 changed the sibling to a sibling's child,
+           the sibling's child can't be red, since no red parent can have a red child). */
+        /* if ((!this.isLeaf(s))
+               && this.isBlack(s)) { */
+
+        /* the following statements just force the red to be on the left of the left of the parent,
+           or right of the right, so case six will rotate correctly. */
+        if (node === p.left
+            && this.isRed(s.left)
+			&& this.isBlack(s.right)) {
+
+            s.color = RED;
+            s.left.color = BLACK;
+            this.rotateRight(s);
+        }
+        else if (node === p.right
+            && this.isBlack(s.left)
+            && this.isRed(s.right)) {
+
+            s.color = RED;
+            s.right.color = BLACK;
+            this.rotateLeft(s);
+        }
+        //}
+        this.eraseCase6(node);
+    }
+
+    /**
+     * @private
+     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
+     * @param {*} node
+     */
+    eraseCase6(node) {
+        let s = node.sibling();
+        let p = node.parent;
+        s.color = this.fetchColor(p);
+        p.color = BLACK;
+
+        if (node === p.left) {
+            s.right.color = BLACK;
+            this.rotateLeft(p);
+        }
+        else {
+            s.left.color = BLACK;
+            this.rotateRight(p);
+        }
+    }
+
+    /* ===========================
+       SEARCH BY KEY
+       =========================== */
+    /**
+    * @returns an iterator pointin to a node with matching key value. If node is not found then end() iterator is returned.
+    * @param {*} k - key value
+    */
+    find(k) {
+        let y = this.head;
+        let x = y.root;
+        while (!this.isLeaf(x)) {
+            let rc = this.compare(x.key, k);
+            if (rc > 0) {
+                y = x;
+                x = x.left;
+            }
+            else if (rc < 0) {
+                y = x;
+                x = x.right;
+            }
+            else {
+                return new Iterator(x, this);
+            }
+        }
+        return new Iterator(this.head, this);
+    }
+
+    /**
+     * @returns an iterator pointing to the first node in the tree that is not less than
+     * (i.e. greater or equal to) the specified key value, or end() if no such node is found.
+     * @param {*} k - key value
+     */
+    lowerBound(k) {
+        let y = this.head;
+        let x = y.root;
+        while (!this.isLeaf(x)) {
+            let rc = this.compare(x.key, k);
+            if (rc >= 0) {
+                y = x;
+                x = x.left;
+            }
+            else {
+                x = x.right;
+            }
+        }
+        return new Iterator(y, this);
+    }
+
+    /**
+     * @returns an iterator pointing to the first node in the tree that is greater than
+     * the specified key value, or end() if no such node is found.
+     * @param {*} k - key value
+     */
+    upperBound(k) {
+        let y = this.head;
+        let x = y.root;
+        while (!this.isLeaf(x)) {
+            let rc = this.compare(x.key, k);
+            if (rc > 0) {
+                y = x;
+                x = x.left;
+            }
+            else {
+                x = x.right;
+            }
+        }
+        return new Iterator(y, this);
+    }
+
+    /* ===========================
+       ITERATORS
+       =========================== */
+
+    /**
+     * @returns iterator pointing to the node with the lowest key
+     */
+    begin() {
+        return new Iterator(this.head.leftmost, this);
+    }
+
+    /**
+     * @returns iterator pointing to the node following the node with the highest key
+     */
+    end() {
+        return new Iterator(this.head, this);
+    }
+
+    /**
+     * @returns iterator pointing to the node with the highest key
+     */
+    rbegin() {
+        return new ReverseIterator(this.head.rightmost, this);
+    }
+
+    /**
+     * @returns iterator pointing to the node preceding the node with the lowest key
+     */
+    rend() {
+        return new ReverseIterator(this.head, this);
+    }
+
+    /**
+     * @private
+     * provides support for ES6 forward iteration
+     */
+    jsBegin() {
+        return this.head.leftmost;
+    }
+
+    /**
+     * @private
+     * provides support for ES6 forward iteration
+     */
+    jsEnd() {
+        return this.head;
+    }
+
+    /**
+     * @private
+     * provides support for ES6 reverse iteration
+     */
+    jsRbegin() {
+        return this.head.rightmost;
+    }
+
+    /**
+     * @private
+     * provides support for ES6 forward iteration
+     */
+    jsRend() {
+        return this.head;
+    }
+
+    /**
+     * @returns node following the specified node in ascending order of their keys
+     * @param {*} n - node
+     */
+    next(n) {
+        if (n === this.head) {
+            return this.head.leftmost;
+        }
+        if (n.right === this.head) {
+            return this.head;
+        }
+        if (n.right !== null) {
+            let res = this.fetchMinimum(n.right);
+            return res;
+        }
+        else {
+            while (n.parent.left !== n) {
+                n = n.parent;
+            }
+            return n.parent;
+        }
+    }
+
+    /**
+     * @returns node preceding the specified node in ascending order of their keys
+     * @param {*} n - node
+     */
+    prev(n) {
+        if (n === this.head) {
+            return this.head.rightmost;
+        }
+        if (n.left === this.head) {
+            return this.head;
+        }
+        if (n.left !== null) {
+            let res = this.fetchMaximum(n.left);
+            return res;
+        }
+        else {
+            while (n.parent.right !== n) {
+                n = n.parent;
+            }
+            return n.parent;
+        }
+    }
+
+    /**
+     * ES6 forward iteration
+     */
+    [Symbol.iterator]() {
+        return new JsIterator(this);
+    }
+
+    /**
+     * ES6 reverse iteration
+     */
+    backward() {
+        return new JsReverseIterator(this);
+    }
+
+    /**
+     * @returns a new JsIterator object that contains the [key, value] pairs for each element in the order of the keys.
+     */
+    entries() {
+        return new JsIterator(this);
+    }
+
+    /**
+     * @returns a new JsIterator object that contains the keys for each element in the order of the keys.
+     */
+    keys() {
+        return new JsIterator(this, new KeyOnlyPolicy());
+    }
+
+    /**
+     * @returns a new JsIterator object that contains the values for each element in the order of the keys.
+     */
+    values() {
+        return new JsIterator(this, new ValueOnlyPolicy());
+    }
+
+    /**
+     * @returns first element of the container, or undefined if container is empty
+     */
+    first() {
+        if (this.size() === 0) {
+            return undefined;
+        }
+        else {
+            let it = this.begin();
+            return this.valuePolicy.fetch(it.node);
+        }
+    }
+
+    /**
+     * @returns last element of the container, or undefined if container is empty
+     */
+    last() {
+        if (this.size() === 0) {
+            return undefined;
+        }
+        else {
+            let it = this.rbegin();
+            return this.valuePolicy.fetch(it.node);
+        }
+    }
+
+    /**
+     * @returns String representation of the container
+     */
+    toString() {
+        let parts = [];
+        for (let it = this.begin(); !it.equals(this.end()); it.next()) {
+            // convert each key-value pair
+            parts.push(this.valuePolicy.toString(it.node));
+        }
+        return '{' + parts.join(',') + '}';
+    }
+
+    /**
+     * @returns String tag of this class
+     */
+    get [Symbol.toStringTag]() {
+        return 'Tree';
+    }
+
+    /**
+     * @returns constructor object for this class
+     */
+    static get [Symbol.species]() {
+        return Tree;
+    }
+
+}
+
+module.exports = {
+    Tree: Tree,
+    compare: compare
+};
+
+
+/***/ }),
+/* 3 */
+/***/ (function(module, exports, __nested_webpack_require_31214__) {
+
+"use strict";
+
+
+/* Containers are expected to support the following methods:
+   jsBegin() - returns the very first node
+   jsEnd() - returns the node beyond the last one
+   next(node) - returns the next node
+   prev(node) - returns the previous node
+   valuePolicy - an instance of KeyOnlyPolicy, or KeyValuePolicy */
+/**
+  * ES6-style forward iterator.
+  *
+  * @example
+  * let m = new TreeMap();
+  * ...
+  * for (let [key, value] of m) {
+  *   console.log(`key: ${key}, value: ${value}`);
+  * }
+  * // iterate values
+  * for (let value of m.values()) {
+  *   console.log(`value: ${value}`);
+  * }
+  */
+class JsIterator {
+    /**
+     * @param {*} container
+     */
+    constructor(container, valuePolicy = container.valuePolicy) {
+        /**
+         * @private
+         * Internal reference to a container
+         */
+        this.container = container;
+        /**
+         * @private
+         * valuePolicy implements what members of the node will be returned: key, value, or key and value
+         */
+        this.valuePolicy = valuePolicy;
+        /**
+         * @private
+         * current node
+         */
+        this.node = container.jsBegin();
+    }
+
+    /**
+     * As documented in ES6 iteration protocol. It can be used for manual iteration.
+     * Iterators are documented here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
+     *
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * let jsIt = m.entries();
+     * while (true) {
+     *   let res = it.next();
+     *   if (res.done) {
+     *     break;
+     *   }
+     *   console.log(`key: ${res.value[0]}, value: ${res.value[1]`});
+     * }
+     */
+    next() {
+        let res = {};
+        res.done = (this.node === this.container.jsEnd());
+        if (!res.done) {
+            res.value = this.valuePolicy.fetch(this.node);
+            this.node = this.container.next(this.node);
+        }
+        return res;
+    }
+
+    /**
+     * Support for ES6 for-of loops.
+     * @returns {JsIterator}
+     */
+    [Symbol.iterator]() {
+        return this;
+    }
+
+    /**
+     * A reverse iterator for the same container.
+     * @returns {JsReverseIterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * // iterate all key-value pairs in reverse order
+     * for (let [key, value] of m.backwards()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+    */
+    backwards() {
+        // eslint-disable-next-line no-use-before-define
+        return new JsReverseIterator(this.container, this.valuePolicy);
+    }
+}
+
+/* Containers are expected to support the following methods:
+   jsRbegin() - returns the very first node in reverse order (e.g. the very last node)
+   jsrEnd() - returns the node beyond the last one in reverse order (e.g. the node before the first one)
+   next(node) - returns the next node
+   prev(node) - returns the previous node
+   valuePolicy - an instance of KeyOnlyPolicy, or KeyValuePolicy */
+/**
+  * ES6-style backward iterator
+  * @example
+  * let m = new TreeMap();
+  * ...
+  * // iterate all key-value pairs in reverse order
+  * for (let [key, value] of m.backwards()) {
+  *   console.log(`key: ${key}, value: ${value}`);
+  * }
+  * // iterate keys in reverse order
+  * for (let key of m.keys().backwards()) {
+  *   console.log(`key: ${key}`);
+  * }
+ */
+class JsReverseIterator {
+    /**
+     * @param {*} container
+     */
+    constructor(container, valuePolicy = container.valuePolicy) {
+        /**
+         * @private
+         * Internal reference to a container
+         */
+        this.container = container;
+        /**
+         * @private
+         * valuePolicy implements what members of the node will be returned: key, value, or key and value
+         */
+        this.valuePolicy = valuePolicy;
+        /**
+         * @private
+         * current node
+         */
+        this.node = container.jsRbegin();
+    }
+
+    /**
+     * As documented in ES6 iteration protocol. It can be used for manual iteration.
+     * Iterators are documented here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
+     *
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * let jsIt = m.entries().backwards();
+     * while (true) {
+     *   let res = it.next();
+     *   if (res.done) {
+     *     break;
+     *   }
+     *   console.log(`key: ${res.value[0]}, value: ${res.value[1]`});
+     * }
+     */
+    next() {
+        let res = {};
+        res.done = (this.node === this.container.jsRend());
+        if (!res.done) {
+            res.value = this.valuePolicy.fetch(this.node);
+            this.node = this.container.prev(this.node);
+        }
+        return res;
+    }
+
+    /**
+     * Support for ES6 for-of loops.
+     * @returns {JsReverseIterator}
+     */
+    [Symbol.iterator]() {
+        return this;
+    }
+
+    /**
+     * A forward iterator for the same container
+     * @returns {JsIterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * // iterate all key-value pairs in direct order
+     * for (let [key, value] of m.backwards().backwards()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     */
+    backwards() {
+        return new JsIterator(this.container, this.valuePolicy);
+    }
+}
+
+module.exports = {
+    JsIterator: JsIterator,
+    JsReverseIterator: JsReverseIterator
+};
+
+/***/ }),
+/* 4 */
+/***/ (function(module, exports, __nested_webpack_require_36806__) {
+
+"use strict";
+
+/**
+ * Base class for STL-like iterators. It references a node (or index) and a container.
+ * Navigation is achieved by calling container's prev() and next() methods.
+ */
+class BaseIterator {
+    /**
+     * @param {*} node - current node
+     * @param {*} container - container
+     */
+    constructor(node, container) {
+        /**
+         * @private
+         * __n - internal node reference
+         */
+        this.__n = node;
+        /**
+         * @private
+         * __c - internal container reference
+         */
+        this.__c = container;
+    }
+
+    /**
+     * Two iterators are considered to be equal if they point to the same node of the same container
+     * @param {BaseIterator} rhs - object on the 'right-hand side' of .eq. operator
+     * @returns {boolean}
+     */
+    equals(rhs) {
+        let lhsClass = this.constructor.name;
+        let rhsClass = rhs.constructor.name;
+        if (lhsClass !== rhsClass) {
+            throw new Error(`Can't compare an instance of ${lhsClass} with an instance of ${rhsClass}`);
+        }
+        if (this.__c !== rhs.__c) {
+            throw new Error('Iterators belong to different containers');
+        }
+        return this.__n === rhs.__n;
+    }
+
+    /**
+     * @private
+     * @returns current node
+     */
+    get node() {
+        return this.__n;
+    }
+
+    /**
+     * @private
+     * @returns key of the current node
+     */
+    get key() {
+        return this.__n.key;
+    }
+
+    /**
+     * @private
+     * @returns value of the current node
+     */
+    get value() {
+        return this.__n.value;
+    }
+
+    /**
+     * @private
+     * @returns container that holds current node
+     */
+    get container() {
+        return this.__c;
+    }
+}
+
+/**
+ * STL-like forward iterator. It's more verbose than ES6 iterators, but allows iteration over any part of the container
+ *
+ * @example
+ * let m = new TreeMap();
+ * ...
+ * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
+ *   console.log(`key: ${it.key}, value: ${it.value}`);
+ * }
+ */
+class Iterator extends BaseIterator {
+    /**
+     * There are 3 ways to construct an iterator:
+     *
+     * 1. Using a node and a container
+     * 2. Copy constructor / clone
+     * 3. Copy constructor / clone from ReverseIterator instance
+     * @param {*} args
+     *
+     * @example
+     * // Using a node and a container
+     * let it = new Iterator(node, container);
+     *
+     * // Copy constructor / clone
+     * let it1 = new Iterator(node, container);
+     * let it2 = new Iterator(it1);
+     *
+     * // Copy constructor / clone from ReverseIterator instance
+     * let it1 = new ReverseIterator(node, container);
+     * let it2 = new Iterator(it1);
+     */
+    constructor(...args) {
+        if (args.length === 2) {
+            let [node, container] = args;
+            super(node, container);
+        }
+        else if (args.length === 1) {
+            let [obj] = args;
+            let className = obj.constructor.name;
+            if (className === Iterator.name) {
+                super(obj.__n, obj.__c);
+            }
+            // eslint-disable-next-line no-use-before-define
+            else if (className === ReverseIterator.name) {
+                let c = obj.__c;
+                super(c.next(obj.__n), c);
+            }
+            else {
+                throw new Error(`Can't create an Iterator from ${className}`);
+            }
+        }
+        else {
+            throw new Error('Can\'t create an Iterator with provided parameters');
+        }
+    }
+
+    /**
+     * Replaces node reference with the reference of the next node in the container.
+     * Can be used for manual iteration over a range of key-value pairs.
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * let from = t.lowerBound(0);
+     * let to = t.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    next() {
+        /**
+         * __n and __c are defined in the base class
+         */
+        this.__n = this.__c.next(this.__n);
+    }
+
+    /**
+     * Replaces node reference with the reference of the previous node in the container
+     * Can be used for manual reverse iteration over a range of key-value pairs.
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * let from = t.lowerBound(0);
+     * let to = t.upperBound(50);
+     * let it = to;
+     * while (!it.equals(from)) {
+     *   it.prev();
+     *   console.log(it.key);
+     * }
+     */
+    prev() {
+        this.__n = this.__c.prev(this.__n);
+    }
+}
+
+/**
+ * STL-like backward iterator. Can be used to traverse container or a range in the reverse order.
+ * It's more verbose than ES6 iterators, but allows iteration over any part of the container
+ *
+ * @example
+ * let m = new TreeMap();
+ * ...
+ * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
+ *   console.log(`key: ${it.key}, value: ${it.value}`);
+ * }
+ */
+class ReverseIterator extends BaseIterator {
+    /**
+     * There are 3 ways to construct a reverse iterator:
+     *
+     * 1. Using a node and a container
+     * 2. Copy constructor / clone
+     * 3. Copy constructor / clone from forward Iterator instance
+     * @param {*} args
+     *
+     * @example
+     * // Using a node and a container
+     * let it = new ReverseIterator(node, container);
+     *
+     * // Copy constructor / clone
+     * let it1 = new ReverseIterator(node, container);
+     * let it2 = new ReverseIterator(it1);
+     *
+     * // Copy constructor / clone from forward Iterator instance
+     * let it1 = new Iterator(node, container);
+     * let it2 = new ReverseIterator(it1);
+     */
+    constructor(...args) {
+        if (args.length === 2) {
+            let [node, container] = args;
+            super(node, container);
+        }
+        else if (args.length === 1) {
+            let [obj] = args;
+            let className = obj.constructor.name;
+            if (className === ReverseIterator.name) {
+                super(obj.__n, obj.__c);
+            }
+            else if (className === Iterator.name) {
+                let c = obj.__c;
+                super(c.prev(obj.__n), c);
+            }
+            else {
+                throw new Error(`Can't create an ReverseIterator from ${className}`);
+            }
+        }
+        else {
+            throw new Error('Can\'t create a Reverse Iterator with provided parameters');
+        }
+    }
+
+    /**
+     *  Replaces node reference with the reference of the previous node in the container, because it works in reverse order
+     * Can be used for manual reverse iteration over a range of key-value pairs.
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * let from = new ReverseIterator(t.upperBound(50));
+     * let to = new ReverseIterator(t.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    next() {
+        /**
+         * __n and __c are defined in the base class
+         */
+        this.__n = this.__c.prev(this.__n);
+    }
+
+    /**
+     *  Replaces node reference with the reference of the next node in the container, because it works in reverse order
+     * Can be used for manual forward iteration over a range of key-value pairs.
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * let from = new ReverseIterator(t.upperBound(50));
+     * let to = new ReverseIterator(t.lowerBound(0));
+     * let it = to;
+     * while (!it.equals(from)) {
+     *   it.prev();
+     *   console.log(it.key);
+     * }
+     */
+    prev() {
+        this.__n = this.__c.next(this.__n);
+    }
+}
+
+module.exports = {
+    Iterator: Iterator,
+    ReverseIterator: ReverseIterator
+};
+
+/***/ }),
+/* 5 */
+/***/ (function(module, exports, __nested_webpack_require_45031__) {
+
+module.exports = __nested_webpack_require_45031__(6);
+
+
+/***/ }),
+/* 6 */
+/***/ (function(module, exports, __nested_webpack_require_45149__) {
+
+/* This is an entry point to the library.
+   It collects all public classes and re-exports them */
+/**@private */
+const {TreeMap} = __nested_webpack_require_45149__(7);
+/**@private */
+const {TreeMultiMap} = __nested_webpack_require_45149__(9);
+/**@private */
+const {TreeSet} = __nested_webpack_require_45149__(10);
+/**@private */
+const {TreeMultiSet} = __nested_webpack_require_45149__(11);
+/**@private */
+const {Iterator, ReverseIterator} = __nested_webpack_require_45149__(4);
+/**@private */
+const {JsIterator, JsReverseIterator} = __nested_webpack_require_45149__(3);
+
+module.exports = {
+    Iterator: Iterator,
+    ReverseIterator: ReverseIterator,
+    JsIterator: JsIterator,
+    JsReverseIterator: JsReverseIterator,
+    TreeMap: TreeMap,
+    TreeMultiMap: TreeMultiMap,
+    TreeSet: TreeSet,
+    TreeMultiSet: TreeMultiSet,
+};
+
+/***/ }),
+/* 7 */
+/***/ (function(module, exports, __nested_webpack_require_46005__) {
+
+/** An implementation of red-black tree */
+const {Tree} = __nested_webpack_require_46005__(2);
+/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
+const {KeyValuePolicy} = __nested_webpack_require_46005__(1);
+/** Node for a red-black tree */
+const {TreeNode} = __nested_webpack_require_46005__(0);
+
+/**
+ * TreeMap is an associative container that stores elements formed
+ * by a combination of a key value and a mapped value, following a specific order.
+ *
+ * In a TreeMap, the key values are generally used to sort and uniquely identify
+ * the elements, while the mapped values store the content associated to this key.
+ * The types of key and mapped value may differ.
+ *
+ * ## Container properties
+ * * **Associative** - Elements in associative containers are referenced by their key
+ * and not by their absolute position in the container.
+ * * **Ordered** - The elements in the container follow a strict order at all times.
+ * All inserted elements are given a position in this order.
+ * * **Map** - Each element associates a key to a mapped value. Keys are meant
+ * to identify the elements whose main content is the mapped value.
+ * * **Unique keys** - No two elements in the container can have equivalent keys.
+ *
+ * @example
+ * let map = new TreeMap();
+ * // add few values
+ * map.set(1, 'a');
+ * map.set(2, 'b');
+ * // find a value by key
+ * let v = map.get(1); // << 'a'
+ * // print all key-value pairs
+ * for (let [key, value] of map) {
+ *   console.log(`key: ${key}, value: ${value}`);
+ * }
+ */
+class TreeMap {
+    /*======================================================
+     * Methods of ES6 Map
+     *======================================================*/
+
+    /**
+     * Creates an empty, or a pre-initialized map.
+     * @param {*} [iterable] Another iterable object whose key-value pairs are added into the newly created map.
+     * @example
+     * // Create an empty map
+     * let map1 = new TreeMap();
+     * // Create and initialize map
+     * let map2 = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     */
+    constructor(iterable) {
+        /** Internal tree */
+        this.__t = new Tree();
+        this.__t.valuePolicy = new KeyValuePolicy();
+        if ((iterable !== undefined)
+            && (iterable !== null)) {
+            if (iterable[Symbol.iterator] !== undefined) {
+                // copy contents
+                for (let [k, v] of iterable) {
+                    this.set(k, v);
+                }
+            }
+            else {
+                throw new Error('TreeMap constructor accepts only iterable objects');
+            }
+        }
+    }
+
+    /**
+     * String tag of this class
+     * @returns {String}
+     * @example
+     * Object.prototype.toString.call(new TreeMap()); // "[object TreeMap]"
+     */
+    get [Symbol.toStringTag]() {
+        return 'TreeMap';
+    }
+
+    /**
+     * Allows to create programmatically an instance of the same class
+     * @returns constructor object for this class.
+     * @example
+     * let map = new TreeMap();
+     * let constrFunc = Object.getPrototypeOf(map).constructor[Symbol.species];
+     * let map2 = new constrFunc();
+     */
+    static get [Symbol.species]() {
+        return TreeMap;
+    }
+
+    /**
+     * Removes all key-value pairs.
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.clear();
+     * console.log(map.size); // 0
+     */
+    clear() {
+        this.__t.clear();
+    }
+
+    /**
+     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.delete(2);
+     * console.log(map.toString()); // {1:A,3:C}
+     */
+    delete(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            this.__t.erase(it.node);
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all key-value pairs in ascending order of the keys.
+     * @returns {JsIterator}
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map.entries()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    entries() {
+        return this.__t.entries();
+    }
+
+    /**
+     * Iterates all key-value pairs using a callback in ascending order of the keys.
+     * Note that ES6 specifies the order of key value parameters in the callback differently from for-of loop.
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.forEach(function(value, key, container) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * });
+     */
+    forEach(callback) {
+        for (let [k, v] of this.__t) {
+            callback(v, k, this);
+        }
+    }
+
+    /**
+     * Finds value associated with the specified key. If specified key does not exist then undefined is returned.
+     * @returns {*}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let v = map.get(3); // 'C'
+     * * let v = map.get(4); // returns undefined
+     */
+    get(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return it.value;
+        }
+        else {
+            return undefined;
+        }
+    }
+
+    /**
+     * A boolean indicator whether map contains a key-value pair with the specified key
+     * @returns {Boolean}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let b = map.get(3); // true
+     */
+    has(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order of the keys.
+     * @returns {JsIterator}
+     * @example
+     * // iterate all keys
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let k of map.keys()) {
+     *   console.log(k); // 1, 2, 3
+     * }
+     * // iterate all keys in reverse order
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let k of map.keys().backward()) {
+     *   console.log(k); // 3, 2, 1
+     * }
+     */
+    keys() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Adds or updates key-value pair to the map.
+     * @param {*} key
+     * @param {*} value
+     * @example
+     * let map = new TreeMap();
+     * map.set(1, 'A');
+     */
+    set(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        this.__t.insertOrReplace(n);
+    }
+
+    /**
+     * Number of key-value pairs in the map.
+     * @returns {Number}
+     */
+    get size() {
+        return this.__t.size();
+    }
+
+    /**
+     * Forward ES6 iterator for all values in ascending order of the keys.
+     * @returns {JsITerator}
+     * @example
+     * // iterate all values
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let v of map.values()) {
+     *   console.log(v); // 'A', 'B', 'C'
+     * }
+     * // iterate all values in reverse order
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let v of map.values().backward()) {
+     *   console.log(v); // 'C', 'B', 'A'
+     * }
+     */
+    values() {
+        return this.__t.values();
+    }
+
+    /**
+     * Forward ES6 iterator for all key-value pairs in ascending order of the keys. The same as entries() method
+     * @returns {JsIterator}
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    [Symbol.iterator]() {
+        return this.__t[Symbol.iterator]();
+    }
+
+    /*======================================================
+     * More methods
+     *======================================================*/
+    /**
+     * ES6 reverse iterator for all key-value pairs in descending order of the keys.
+     * @returns {JsReverseIterator}
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map.backwards()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    backward() {
+        return this.__t.backward();
+    }
+
+    /**
+     * Sets custom comparison function if key values are not of primitive types.
+     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
+     *      +1 if the value of rhs is greater than lhs
+     *      -1 if the value of rhs is less than lhs
+     *       0 if values are the same
+     */
+    set compareFunc(func) {
+        this.clear();
+        this.__t.compare = func;
+    }
+
+    /*======================================================
+     * STL-like methods
+     *======================================================*/
+
+    /**
+     * Forward iterator to the first element
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    begin() {
+        return this.__t.begin();
+    }
+
+    /**
+     * Forward iterator to the element following the last element
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    end() {
+        return this.__t.end();
+    }
+
+    /**
+     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * ...
+     * let it = m.find(1);
+     * if (!it.equals(m.end())) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`); // 1, 'A'
+     * }
+     */
+    find(key) {
+        return this.__t.find(key);
+    }
+
+    /**
+     * Adds key-value pair if such key does not exist in the map
+     * @param {*} key
+     * @param {*} value
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let m = new TreeMap();
+     * let res = m.insertUnique(1, 'A');
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints A
+     * }
+     * res = m.insertUnique(1, 'B') // this step has no effect on the map
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // not executed
+     * }
+     */
+    insertUnique(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        return this.__t.insertUnique(n);
+    }
+
+    /**
+     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
+     * @param {*} key
+     * @param {*} value
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let m = new TreeMap();
+     * let res = m.insertOrReplace(1, 'A');
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints A
+     * }
+     * res = m.insertOrReplace(1, 'B') // replaces value on the existing node
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // prints B
+     * }
+     */
+    insertOrReplace(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        return this.__t.insertOrReplace(n);
+    }
+
+    /**
+     * Removes key-value pair for the specified iterator.
+     * @param {Iterator} iterator
+     * @example
+     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let it = map.find(2);
+     * it.prev();
+     * map.erase(it); // removes a node with key 1
+     * console.log(map.toString()); // {2:B,3:C}
+     */
+    erase(iterator) {
+        this.__t.erase(iterator.node);
+    }
+
+    /**
+     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = m.lowerBound(0);
+     * let to = m.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(m.upperBound(50));
+     * let to = new ReverseIterator(m.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    lowerBound(key) {
+        return this.__t.lowerBound(key);
+    }
+
+    /**
+     * Reverse iterator to the last element.
+     * @returns {ReverseIterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    rbegin() {
+        return this.__t.rbegin();
+    }
+
+    /**
+     * Reverse iterator pointing to before the first element.
+     * @returns {ReverseIterator}
+     * @example
+     * let m = new TreeMap();
+     * ...
+     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    rend() {
+        return this.__t.rend();
+    }
+
+    /**
+     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = m.lowerBound(0);
+     * let to = m.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let m = new TreeMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(m.upperBound(50));
+     * let to = new ReverseIterator(m.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    upperBound(key) {
+        return this.__t.upperBound(key);
+    }
+
+    /**
+     * @returns first key/value pair of the container, or undefined if container is empty
+     * @example
+     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let first = m.first();
+     * if (first) {
+     *   let key = first[0];   // 1
+     *   let value = first[1]; // 'A'
+     * }
+     */
+    first() {
+        return this.__t.first();
+    }
+
+    /**
+     * @returns last key/value pair of the container, or undefined if container is empty
+     * @example
+     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let last = m.last();
+     * if (last) {
+     *   let key = last[0];   // 3
+     *   let value = last[1]; // 'C'
+     * }
+     */
+    last() {
+        return this.__t.last();
+    }
+
+    /**
+     * Serializes contents of the map in the form {key1:value1,key2:value2,...}
+     * @returns {String}
+     */
+    toString() {
+        return this.__t.toString();
+    }
+}
+
+module.exports = {
+    TreeMap: TreeMap,
+};
+
+/***/ }),
+/* 8 */
+/***/ (function(module, exports) {
+
+/**
+ * An instance of this class reports whether insert operation was successful.
+ * if a node was added, or an existing one replaced then an iterator is provided. Otherwise the value of iterator is undefined
+ */
+class InsertionResult {
+    /**
+     * Default constructor
+     * @param {Boolean} wasAdded
+     * @param {Boolean} wasReplaced
+     * @param {Iterator} iterator only provided if the node was added, or replaced
+     */
+    constructor(wasAdded, wasReplaced, iterator) {
+        /**
+         * Boolean flag indicating whether an element was added
+         */
+        this.wasAdded = wasAdded;
+        /**
+         * Boolean flag indicating whether an existing node was updated
+         */
+        this.wasReplaced = wasReplaced;
+        /**
+         * {Iterator} instance pointing to the newly added node
+         */
+        this.iterator = iterator;
+    }
+}
+
+module.exports = {
+    InsertionResult: InsertionResult
+};
+
+/***/ }),
+/* 9 */
+/***/ (function(module, exports, __nested_webpack_require_63461__) {
+
+/** An implementation of red-black tree */
+const {Tree} = __nested_webpack_require_63461__(2);
+/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
+const {KeyValuePolicy} = __nested_webpack_require_63461__(1);
+/** Node for a red-black tree */
+const {TreeNode} = __nested_webpack_require_63461__(0);
+
+/**
+ * TreeMultiMap is an associative container that stores elements formed by
+ * a combination of a key value and a mapped value, following a specific order,
+ * and where multiple elements can have equivalent keys.
+ *
+ * In a TreeMultiMap, the key values are generally used to sort and uniquely
+ * identify the elements, while the mapped values store the content
+ * associated to this key. The types of key and mapped value may differ.
+ *
+ * ## Container properties
+ * * **Associative** - Elements in associative containers are referenced
+ * by their key and not by their absolute position in the container.
+ * * **Ordered** - The elements in the container follow a strict order
+ * at all times. All inserted elements are given a position in this order.
+ * * **Map** - Each element associates a key to a mapped value. Keys are meant
+ * to identify the elements whose main content is the mapped value.
+ * * **Multiple equivalent keys** - Multiple elements in the container
+ * can have equivalent keys.
+ *
+ * @example
+ * let map = new TreeMultiMap();
+ * // add few values
+ * map.set(1, 'a');
+ * map.set(2, 'b');
+ * map.set(2, 'c');
+ * // find a value by key
+ * let v = map.get(1); // << 'a'
+ * find all values for a given key
+ * // print all key-value pairs
+ * let from = map.lowerBound(2);
+ * let to = map.upperBound(2);
+ * let it = from;
+ * while (!it.equals(to)) {
+ *   console.log(it.key);
+ *   it.next();
+ * }
+ */
+class TreeMultiMap {
+    /*======================================================
+     * Methods of ES6 Map
+     *======================================================*/
+
+    /**
+     * Creates an empty, or a pre-initialized map.
+     * @param {*} [iterable] Another iterable object whose key-value pairs are added into the newly created map.
+     * @example
+     * // Create an empty map
+     * let map1 = new TreeMultiMap();
+     * // Create and initialize map
+     * let map2 = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     */
+    constructor(iterable) {
+        /** Internal tree */
+        this.__t = new Tree();
+        this.__t.valuePolicy = new KeyValuePolicy();
+        if ((iterable !== undefined)
+            && (iterable !== null)) {
+            if (iterable[Symbol.iterator] !== undefined) {
+                // copy contents
+                for (let [k, v] of iterable) {
+                    this.set(k, v);
+                }
+            }
+            else {
+                throw new Error('TreeMultiMap constructor accepts only iterable objects');
+            }
+        }
+    }
+
+    /**
+     * String tag of this class
+     * @returns {String}
+     * @example
+     * Object.prototype.toString.call(new TreeMultiMap()); // "[object TreeMultiMap]"
+     */
+    get [Symbol.toStringTag]() {
+        return 'TreeMultiMap';
+    }
+
+    /**
+     * Allows to create programmatically an instance of the same class
+     * @returns constructor object for this class.
+     * @example
+     * let map = new TreeMultiMap();
+     * let constrFunc = Object.getPrototypeOf(map).constructor[Symbol.species];
+     * let map2 = new constrFunc();
+     */
+    static get [Symbol.species]() {
+        return TreeMultiMap;
+    }
+
+    /**
+     * Removes all key-value pairs.
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.clear();
+     * console.log(map.size); // 0
+     */
+    clear() {
+        this.__t.clear();
+    }
+
+    /**
+     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.delete(2);
+     * console.log(map.toString()); // {1:A,3:C}
+     */
+    delete(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            this.__t.erase(it.node);
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all key-value pairs in ascending order of the keys.
+     * @returns {JsIterator}
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map.entries()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    entries() {
+        return this.__t.entries();
+    }
+
+    /**
+     * Iterates all key-value pairs using a callback in ascending order of the keys.
+     * Note that ES6 specifies the order of key value parameters in the callback differently from for-of loop.
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * map.forEach(function(value, key, container) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * });
+     */
+    forEach(callback) {
+        for (let [k, v] of this.__t) {
+            callback(v, k, this);
+        }
+    }
+
+    /**
+     * Finds value associated with the specified key. If specified key does not exist then undefined is returned.
+     * @returns {*}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let v = map.get(3); // 'C'
+     * * let v = map.get(4); // returns undefined
+     */
+    get(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return it.value;
+        }
+        else {
+            return undefined;
+        }
+    }
+
+    /**
+     * A boolean indicator whether map contains a key-value pair with the specified key
+     * @returns {Boolean}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let b = map.get(3); // true
+     */
+    has(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order of the keys.
+     * @returns {JsIterator}
+     * @example
+     * // iterate all keys
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let k of map.keys()) {
+     *   console.log(k); // 1, 2, 3
+     * }
+     * // iterate all keys in reverse order
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let k of map.keys().backward()) {
+     *   console.log(k); // 3, 2, 1
+     * }
+     */
+    keys() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Adds a key-value pair to the map. Multiple key-value pairs with the same key are allowed in TreeMultiMap.
+     * @param {*} key
+     * @param {*} value
+     * @example
+     * let map = new TreeMultiMap();
+     * map.set(1, 'A');
+     * map.set(1, 'B');
+     * map.set(2, 'C');
+     * for (let k of map.values()) {
+     *   console.log(k); // A, B, C
+     * }
+     */
+    set(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        this.__t.insertMulti(n);
+    }
+
+    /**
+     * Number of key-value pairs in the map.
+     * @returns {Number}
+     */
+    get size() {
+        return this.__t.size();
+    }
+
+    /**
+     * Forward ES6 iterator for all values in ascending order of the keys.
+     * @returns {JsITerator}
+     * @example
+     * // iterate all values
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let v of map.values()) {
+     *   console.log(v); // 'A', 'B', 'C'
+     * }
+     * // iterate all values in reverse order
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let v of map.values().backward()) {
+     *   console.log(v); // 'C', 'B', 'A'
+     * }
+     */
+    values() {
+        return this.__t.values();
+    }
+
+    /**
+     * Forward ES6 iterator for all key-value pairs in ascending order of the keys. The same as entries() method
+     * @returns {JsIterator}
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    [Symbol.iterator]() {
+        return this.__t[Symbol.iterator]();
+    }
+
+    /*======================================================
+     * More methods
+     *======================================================*/
+    /**
+     * ES6 reverse iterator for all key-value pairs in descending order of the keys.
+     * @returns {JsReverseIterator}
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * for (let [key,value] of map.backwards()) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    backward() {
+        return this.__t.backward();
+    }
+
+    /**
+     * Sets custom comparison function if key values are not of primitive types.
+     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
+     *      +1 if the value of rhs is greater than lhs
+     *      -1 if the value of rhs is less than lhs
+     *       0 if values are the same
+     */
+    set compareFunc(func) {
+        this.clear();
+        this.__t.compare = func;
+    }
+
+    /*======================================================
+     * STL-like methods
+     *======================================================*/
+
+    /**
+     * Forward iterator to the first element
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ...
+     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    begin() {
+        return this.__t.begin();
+    }
+
+    /**
+     * Forward iterator to the element following the last element
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ...
+     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    end() {
+        return this.__t.end();
+    }
+
+    /**
+     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * ...
+     * let it = m.find(1);
+     * if (!it.equals(m.end())) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`); // 1, 'A'
+     * }
+     */
+    find(key) {
+        return this.__t.find(key);
+    }
+
+    /**
+     * Adds key-value pair if such key does not exist in the map
+     * @param {*} key
+     * @param {*} value
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let m = new TreeMultiMap();
+     * let res = m.insertUnique(1, 'A');
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints A
+     * }
+     * res = m.insertUnique(1, 'B') // this step has no effect on the map
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // not executed
+     * }
+     */
+    insertUnique(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        return this.__t.insertUnique(n);
+    }
+
+    /**
+     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
+     * @param {*} key
+     * @param {*} value
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let m = new TreeMultiMap();
+     * let res = m.insertOrReplace(1, 'A');
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints A
+     * }
+     * res = m.insertOrReplace(1, 'B') // replaces value on the existing node
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // prints B
+     * }
+     */
+    insertOrReplace(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        return this.__t.insertOrReplace(n);
+    }
+
+    /**
+     * Adds key-value pair. If such key already exists in the map then adds another node with the same key and a new value.
+     * @param {*} key
+     * @param {*} value
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let m = new TreeMultiMap();
+     * let res = m.insertMulti(1, 'A');
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints A
+     * }
+     * res = m.insertMulti(1, 'B') // adds a new node
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.value}`); // prints B
+     *   it.prev();
+     *   console.log(`Previously inserted ${res.iterator.value}`); // prints A
+     * }
+     */
+    insertMulti(key, value) {
+        let n = new TreeNode();
+        n.key = key;
+        n.value = value;
+        return this.__t.insertMulti(n);
+    }
+
+    /**
+     * Removes key-value pair for the specified iterator.
+     * @param {Iterator} iterator
+     * @example
+     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let it = map.find(2);
+     * it.prev();
+     * map.erase(it); // removes a node with key 1
+     * console.log(map.toString()); // {2:B,3:C}
+     */
+    erase(iterator) {
+        this.__t.erase(iterator.node);
+    }
+
+    /**
+     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = m.lowerBound(0);
+     * let to = m.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let m = new TreeMultiMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(m.upperBound(50));
+     * let to = new ReverseIterator(m.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    lowerBound(key) {
+        return this.__t.lowerBound(key);
+    }
+
+    /**
+     * Reverse iterator to the last element.
+     * @returns {ReverseIterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ...
+     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    rbegin() {
+        return this.__t.rbegin();
+    }
+
+    /**
+     * Reverse iterator pointing to before the first element.
+     * @returns {ReverseIterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ...
+     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
+     *   console.log(`key: ${it.key}, value: ${it.value}`);
+     * }
+     */
+    rend() {
+        return this.__t.rend();
+    }
+
+    /**
+     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let m = new TreeMultiMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = m.lowerBound(0);
+     * let to = m.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let m = new TreeMultiMap();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(m.upperBound(50));
+     * let to = new ReverseIterator(m.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    upperBound(key) {
+        return this.__t.upperBound(key);
+    }
+
+    /**
+     * @returns first key/value pair of the container, or undefined if container is empty
+     * @example
+     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let first = m.first();
+     * if (first) {
+     *   let key = first[0];   // 1
+     *   let value = first[1]; // 'A'
+     * }
+     */
+    first() {
+        return this.__t.first();
+    }
+
+    /**
+     * @returns last key/value pair of the container, or undefined if container is empty
+     * @example
+     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
+     * let last = m.last();
+     * if (last) {
+     *   let key = last[0];   // 3
+     *   let value = last[1]; // 'C'
+     * }
+     */
+    last() {
+        return this.__t.last();
+    }
+
+    /**
+     * Serializes contents of the map in the form {key1:value1,key2:value2,...}
+     * @returns {String}
+     */
+    toString() {
+        return this.__t.toString();
+    }
+}
+
+module.exports = {
+    TreeMultiMap: TreeMultiMap,
+};
+
+/***/ }),
+/* 10 */
+/***/ (function(module, exports, __nested_webpack_require_81431__) {
+
+/** An implementation of red-black tree */
+const {Tree} = __nested_webpack_require_81431__(2);
+/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
+const {KeyOnlyPolicy} = __nested_webpack_require_81431__(1);
+/** Node for a red-black tree */
+const {TreeNode} = __nested_webpack_require_81431__(0);
+
+/**
+ * TreeSet is a container that stores unique elements following a specific order.
+ *
+ * In a TreeSet, the value of an element also identifies it (the value is itself the key),
+ * and each value must be unique. The value of the elements in a TreeSet cannot be modified
+ * once in the container (the elements are immutable), but they can be inserted or removed
+ * from the container.
+ *
+ * ## Container properties
+ * * **Associative** - Elements in associative containers are referenced by their key and
+ * not by their absolute position in the container.</li>
+ * * **Ordered** - The elements in the container follow a strict order at all times.
+ * All inserted elements are given a position in this order.</li>
+ * * **Set** - The value of an element is also the key used to identify it.</li>
+ * * **Unique keys** - No two elements in the container can have equivalent keys.</li>
+ *
+ *
+ * @example
+ * let set = new TreeSet();
+ * // add few values
+ * set.add(1);
+ * set.add(2);
+ * // check whether key exists
+ * let flag = set.has(1); // << true
+ * // print all keys
+ * for (let key of set) {
+ *   console.log(`key: ${key}`);
+ * }
+ */
+class TreeSet {
+    /*======================================================
+     * Methods of ES6 Set
+     *======================================================*/
+
+    /**
+     * Creates an empty, or a pre-initialized set.
+     * @param {*} [iterable] Another iterable object whose values are added into the newly created set.
+     * @example
+     * // Create an empty set
+     * let set = new TreeSet();
+     * // Create and initialize set
+     * let set2 = new TreeSet([1, 2, 3]);
+     */
+    constructor(iterable) {
+        /** Internal tree */
+        this.__t = new Tree();
+        this.__t.valuePolicy = new KeyOnlyPolicy();
+        if ((iterable !== undefined)
+            && (iterable !== null)) {
+            if (iterable[Symbol.iterator] !== undefined) {
+                // copy contents
+                for (let k of iterable) {
+                    this.add(k);
+                }
+            }
+            else {
+                throw new Error('TreeSet constructor accepts only iterable objects');
+            }
+        }
+    }
+
+    /**
+     * String tag of this class
+     * @returns {String}
+     * @example
+     * Object.prototype.toString.call(new TreeSet()); // "[object TreeSet]"
+     */
+    get [Symbol.toStringTag]() {
+        return 'TreeSet';
+    }
+
+    /**
+     * Allows to create programmatically an instance of the same class
+     * @returns constructor object for this class.
+     * @example
+     * let set = new TreeSet();
+     * let constrFunc = Object.getPrototypeOf(set).constructor[Symbol.species];
+     * let set2 = new constrFunc();
+     */
+    static get [Symbol.species]() {
+        return TreeSet;
+    }
+
+    /**
+     * Removes all key-value pairs.
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * set.clear();
+     * console.log(set.size); // 0
+     */
+    clear() {
+        this.__t.clear();
+    }
+
+    /**
+     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * set.delete(2);
+     * console.log(set.toString()); // {1,3}
+     */
+    delete(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            this.__t.erase(it.node);
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all values in ascending order.
+     * @returns {JsIterator}
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let key of set.entries()) {
+     *   console.log(`key: ${key}`);
+     * }
+     */
+    entries() {
+        return this.__t.entries();
+    }
+
+    /**
+     * Iterates all values using a callback in ascending order.
+     * Note that ES6 specifies the order of key parameters in the callback differently from for-of loop.
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * set.forEach(function(value, key, container) {
+     *   // value is the same as key
+     *   console.log(`key: ${key}, value: ${value}`);
+     * });
+     */
+    forEach(callback) {
+        for (let k of this.__t) {
+            callback(k, k, this);
+        }
+    }
+
+    /**
+     * A boolean indicator whether set contains the specified key.
+     * @returns {Boolean}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * let b = set.get(3); // true
+     * b = set.get(4); // false
+     */
+    has(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order.
+     * @returns {JsIterator}
+     * @example
+     * // iterate all keys
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let k of set.keys()) {
+     *   console.log(k); // 1, 2, 3
+     * }
+     * // iterate all keys in reverse order
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let k of set.keys().backward()) {
+     *   console.log(k); // 3, 2, 1
+     * }
+     */
+    keys() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Adds a key to the set, unless the key already exists.
+     * @param {*} key
+     * @example
+     * let set = new TreeSet();
+     * set.add(1);
+     */
+    add(key) {
+        let n = new TreeNode();
+        n.key = key;
+        this.__t.insertUnique(n);
+    }
+
+    /**
+     * Number of keys in the set.
+     * @returns {Number}
+     */
+    get size() {
+        return this.__t.size();
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order. It is the same as keys() method
+     * @returns {JsITerator}
+     * @example
+     * // iterate all values
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let v of set.values()) {
+     *   console.log(v); // '1', '2', '3'
+     * }
+     * // iterate all values in reverse order
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let v of set.values().backward()) {
+     *   console.log(v); // '3', '2', '1'
+     * }
+     */
+    values() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order. The same as entries() method
+     * @returns {JsIterator}
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let key of set) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    [Symbol.iterator]() {
+        return this.__t[Symbol.iterator]();
+    }
+
+    /*======================================================
+     * More methods
+     *======================================================*/
+    /**
+     * ES6 reverse iterator for all keys in descending order.
+     * @returns {JsReverseIterator}
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * for (let key of set.backwards()) {
+     *   console.log(`key: ${key}`);
+     * }
+     */
+    backward() {
+        return this.__t.backward();
+    }
+
+    /**
+     * Sets custom comparison function if key values are not of primitive types.
+     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
+     *      +1 if the value of rhs is greater than lhs
+     *      -1 if the value of rhs is less than lhs
+     *       0 if values are the same
+     */
+    set compareFunc(func) {
+        this.clear();
+        this.__t.compare = func;
+    }
+
+    /*======================================================
+     * STL-like methods
+     *======================================================*/
+
+    /**
+     * Forward iterator to the first element
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeSet();
+     * ...
+     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    begin() {
+        return this.__t.begin();
+    }
+
+    /**
+     * Forward iterator to the element following the last element
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeSet();
+     * ...
+     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    end() {
+        return this.__t.end();
+    }
+
+    /**
+     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * ...
+     * let it = set.find(1);
+     * if (!it.equals(set.end())) {
+     *   console.log(`Found key: ${it.key}`); // 1
+     * }
+     */
+    find(key) {
+        return this.__t.find(key);
+    }
+
+    /**
+     * Adds a key if it doesn't exist
+     * @param {*} key
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let set = new TreeSet();
+     * let res = set.insertUnique(1);
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
+     * }
+     * res = set.insertUnique(1); // this step has no effect on the set
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // not executed
+     * }
+     */
+    insertUnique(key) {
+        let n = new TreeNode();
+        n.key = key;
+        return this.__t.insertUnique(n);
+    }
+
+    /**
+     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
+     * @param {*} key
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let set = new TreeSet();
+     * let res = set.insertOrReplace(1);
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
+     * }
+     * res = set.insertOrReplace(1) // returns iterator to the previously added node
+     * if (res.wasInserted) {
+     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
+     * }
+     */
+    insertOrReplace(key) {
+        let n = new TreeNode();
+        n.key = key;
+        return this.__t.insertOrReplace(n);
+    }
+
+    /**
+     * Removes value for the specified iterator.
+     * @param {Iterator} iterator
+     * @example
+     * let set = new TreeSet([1,2,3]);
+     * let it = set.find(2);
+     * it.prev();
+     * set.erase(it); // removes a node with key 1
+     * console.log(set.toString()); // {2,3}
+     */
+    erase(iterator) {
+        this.__t.erase(iterator.node);
+    }
+
+    /**
+     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = set.lowerBound(0);
+     * let to = set.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let set = new TreeSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(set.upperBound(50));
+     * let to = new ReverseIterator(set.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    lowerBound(key) {
+        return this.__t.lowerBound(key);
+    }
+
+    /**
+     * Reverse iterator to the last element.
+     * @returns {ReverseIterator}
+     * @example
+     * let set = new TreeSet();
+     * ...
+     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    rbegin() {
+        return this.__t.rbegin();
+    }
+
+    /**
+     * Reverse iterator pointing to before the first element.
+     * @returns {ReverseIterator}
+     * @example
+     * let set = new TreeSet();
+     * ...
+     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    rend() {
+        return this.__t.rend();
+    }
+
+    /**
+     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = set.lowerBound(0);
+     * let to = set.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let set = new TreeSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(set.upperBound(50));
+     * let to = new ReverseIterator(set.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    upperBound(key) {
+        return this.__t.upperBound(key);
+    }
+
+    /**
+     * @returns first element of the container, or undefined if container is empty
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * let first = set.first(); // 1
+     */
+    first() {
+        return this.__t.first();
+    }
+
+    /**
+     * @returns last element of the container, or undefined if container is empty
+     * @example
+     * let set = new TreeSet([1, 2, 3]);
+     * let last = set.last(); // 3
+     */
+    last() {
+        return this.__t.last();
+    }
+
+    /**
+     * Serializes contents of the set in the form {key1,key2,...}
+     * @returns {String}
+     */
+    toString() {
+        return this.__t.toString();
+    }
+}
+
+module.exports = {
+    TreeSet: TreeSet,
+};
+
+/***/ }),
+/* 11 */
+/***/ (function(module, exports, __nested_webpack_require_96249__) {
+
+/** An implementation of red-black tree */
+const {Tree} = __nested_webpack_require_96249__(2);
+/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
+const {KeyOnlyPolicy} = __nested_webpack_require_96249__(1);
+/** Node for a red-black tree */
+const {TreeNode} = __nested_webpack_require_96249__(0);
+
+/**
+ * TreeMultiSet is a container that stores elements following a specific order,
+ * and where multiple elements can have equivalent values.
+ *
+ * In a TreeMultiSet, the value of an element also identifies it
+ * (the value is itself the key). The value of the elements in a multiset
+ * cannot be modified once in the container (the elements are always immutable),
+ * but they can be inserted or removed from the container.
+ *
+ * ## Container properties
+ * * **Associative** - Elements in associative containers are referenced
+ * by their key and not by their absolute position in the container.
+ * * **Ordered** - The elements in the container follow a strict order
+ * at all times. All inserted elements are given a position in this order.
+ * * **Set** - The value of an element is also the key used to identify it.
+ * * **Multiple equivalent keys** - Multiple elements in the container
+ * can have equivalent keys.
+ *
+ * @example
+ * let set = new TreeMultiSet();
+ * // add few values
+ * set.add(1);
+ * set.add(2);
+ * set.add(2);
+ * // check whether key exists
+ * let flag = set.has(1); // << true
+ * // print all keys
+ * for (let key of set) {
+ *   console.log(`key: ${key}`); // 1, 2, 2
+ * }
+ */
+class TreeMultiSet {
+    /*======================================================
+     * Methods of ES6 Set
+     *======================================================*/
+
+    /**
+     * Creates an empty, or a pre-initialized set.
+     * @param {*} [iterable] Another iterable object whose values are added into the newly created set.
+     * @example
+     * // Create an empty set
+     * let set = new TreeMultiSet();
+     * // Create and initialize set
+     * let set2 = new TreeMultiSet([1, 2, 3]);
+     */
+    constructor(iterable) {
+        /** Internal tree */
+        this.__t = new Tree();
+        this.__t.valuePolicy = new KeyOnlyPolicy();
+        if ((iterable !== undefined)
+            && (iterable !== null)) {
+            if (iterable[Symbol.iterator] !== undefined) {
+                // copy contents
+                for (let k of iterable) {
+                    this.add(k);
+                }
+            }
+            else {
+                throw new Error('TreeMultiSet constructor accepts only iterable objects');
+            }
+        }
+    }
+
+    /**
+     * String tag of this class
+     * @returns {String}
+     * @example
+     * Object.prototype.toString.call(new TreeMultiSet()); // "[object TreeMultiSet]"
+     */
+    get [Symbol.toStringTag]() {
+        return 'TreeMultiSet';
+    }
+
+    /**
+     * Allows to create programmatically an instance of the same class
+     * @returns constructor object for this class.
+     * @example
+     * let set = new TreeMultiSet();
+     * let constrFunc = Object.getPrototypeOf(set).constructor[Symbol.species];
+     * let set2 = new constrFunc();
+     */
+    static get [Symbol.species]() {
+        return TreeMultiSet;
+    }
+
+    /**
+     * Removes all key-value pairs.
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * set.clear();
+     * console.log(set.size); // 0
+     */
+    clear() {
+        this.__t.clear();
+    }
+
+    /**
+     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
+     * @example
+     * let set = new TreeMultiSet([1, 2, 2, 3]);
+     * set.delete(2);
+     * console.log(set.toString()); // {1,2,3}
+     * set.delete(2); / remove the second copy of the key
+     * console.log(set.toString()); // {1,3}
+     */
+    delete(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            this.__t.erase(it.node);
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all values in ascending order.
+     * @returns {JsIterator}
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let key of set.entries()) {
+     *   console.log(`key: ${key}`);
+     * }
+     */
+    entries() {
+        return this.__t.entries();
+    }
+
+    /**
+     * Iterates all values using a callback in ascending order.
+     * Note that ES6 specifies the order of key parameters in the callback differently from for-of loop.
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * set.forEach(function(value, key, container) {
+     *   // value is the same as key
+     *   console.log(`key: ${key}, value: ${value}`);
+     * });
+     */
+    forEach(callback) {
+        for (let k of this.__t) {
+            callback(k, k, this);
+        }
+    }
+
+    /**
+     * A boolean indicator whether set contains the specified key.
+     * @returns {Boolean}
+     * @param {*} key a value of any type that can be compared with a key
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * let b = set.get(3); // true
+     * b = set.get(4); // false
+     */
+    has(key) {
+        let it = this.__t.find(key);
+        if (!it.equals(this.__t.end())) {
+            return true;
+        }
+        else {
+            return false;
+        }
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order.
+     * @returns {JsIterator}
+     * @example
+     * // iterate all keys
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let k of set.keys()) {
+     *   console.log(k); // 1, 2, 3
+     * }
+     * // iterate all keys in reverse order
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let k of set.keys().backward()) {
+     *   console.log(k); // 3, 2, 1
+     * }
+     */
+    keys() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Adds a key to the set. If the key already exists then another entry with the same value is added.
+     * @param {*} key
+     * @example
+     * let set = new TreeMultiSet();
+     * set.add(1);
+     * set.add(1);
+     * set.add(2);
+     * // print all keys
+     * for (let key of set) {
+     *   console.log(`key: ${key}`); // 1, 1, 2
+     * }
+     */
+    add(key) {
+        let n = new TreeNode();
+        n.key = key;
+        this.__t.insertMulti(n);
+    }
+
+    /**
+     * Number of keys in the set.
+     * @returns {Number}
+     */
+    get size() {
+        return this.__t.size();
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order. It is the same as keys() method
+     * @returns {JsITerator}
+     * @example
+     * // iterate all values
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let v of set.values()) {
+     *   console.log(v); // '1', '2', '3'
+     * }
+     * // iterate all values in reverse order
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let v of set.values().backward()) {
+     *   console.log(v); // '3', '2', '1'
+     * }
+     */
+    values() {
+        return this.__t.keys();
+    }
+
+    /**
+     * Forward ES6 iterator for all keys in ascending order. The same as entries() method
+     * @returns {JsIterator}
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let key of set) {
+     *   console.log(`key: ${key}, value: ${value}`);
+     * }
+     */
+    [Symbol.iterator]() {
+        return this.__t[Symbol.iterator]();
+    }
+
+    /*======================================================
+     * More methods
+     *======================================================*/
+    /**
+     * ES6 reverse iterator for all keys in descending order.
+     * @returns {JsReverseIterator}
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * for (let key of set.backwards()) {
+     *   console.log(`key: ${key}`);
+     * }
+     */
+    backward() {
+        return this.__t.backward();
+    }
+
+    /**
+     * Sets custom comparison function if key values are not of primitive types.
+     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
+     *      +1 if the value of rhs is greater than lhs
+     *      -1 if the value of rhs is less than lhs
+     *       0 if values are the same
+     */
+    set compareFunc(func) {
+        this.clear();
+        this.__t.compare = func;
+    }
+
+    /*======================================================
+     * STL-like methods
+     *======================================================*/
+
+    /**
+     * Forward iterator to the first element
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ...
+     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    begin() {
+        return this.__t.begin();
+    }
+
+    /**
+     * Forward iterator to the element following the last element
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ...
+     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    end() {
+        return this.__t.end();
+    }
+
+    /**
+     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * ...
+     * let it = set.find(1);
+     * if (!it.equals(set.end())) {
+     *   console.log(`Found key: ${it.key}`); // 1
+     * }
+     */
+    find(key) {
+        return this.__t.find(key);
+    }
+
+    /**
+     * Adds key if such key does not exist in the set.
+     * @param {*} key
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let set = new TreeMultiSet();
+     * set.insertUnique(1);
+     * set.insertUnique(1); // this step has no effect on the set
+     * let flag = set.has(1); // true
+     * let size = set.size; // 1
+     */
+    insertUnique(key) {
+        let n = new TreeNode();
+        n.key = key;
+        return this.__t.insertUnique(n);
+    }
+
+    /**
+     * Adds key if such key does not exist in the set. Same as insertUnique()
+     * @param {*} key
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let set = new TreeMultiSet();
+     * set.insertOrReplace(1);
+     * set.insertOrReplace(1); // this step has no effect on the set
+     * let flag = set.has(1); // true
+     * let size = set.size; // 1
+     */
+    insertOrReplace(key) {
+        let n = new TreeNode();
+        n.key = key;
+        return this.__t.insertOrReplace(n);
+    }
+
+    /**
+     * Adds key whether it exists or not in the set.
+     * @param {*} key
+     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
+     * @example
+     * let set = new TreeMultiSet();
+     * set.insertMulti(1);
+     * set.insertMulti(1); // this step has no effect on the map
+     * let flag = set.has(1); // true
+     * let size = set.size; // 2
+     */
+    insertMulti(key) {
+        let n = new TreeNode();
+        n.key = key;
+        return this.__t.insertMulti(n);
+    }
+
+    /**
+     * Removes value for the specified iterator.
+     * @param {Iterator} iterator
+     * @example
+     * let set = new TreeMultiSet([1,2,3]);
+     * let it = set.find(2);
+     * it.prev();
+     * set.erase(it); // removes a node with key 1
+     * console.log(set.toString()); // {2,3}
+     */
+    erase(iterator) {
+        this.__t.erase(iterator.node);
+    }
+
+    /**
+     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = set.lowerBound(0);
+     * let to = set.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let set = new TreeMultiSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(set.upperBound(50));
+     * let to = new ReverseIterator(set.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    lowerBound(key) {
+        return this.__t.lowerBound(key);
+    }
+
+    /**
+     * Reverse iterator to the last element.
+     * @returns {ReverseIterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ...
+     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    rbegin() {
+        return this.__t.rbegin();
+    }
+
+    /**
+     * Reverse iterator pointing to before the first element.
+     * @returns {ReverseIterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ...
+     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
+     *   console.log(`key: ${it.key}`);
+     * }
+     */
+    rend() {
+        return this.__t.rend();
+    }
+
+    /**
+     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
+     * @param {*} key
+     * @returns {Iterator}
+     * @example
+     * let set = new TreeMultiSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
+     * let from = set.lowerBound(0);
+     * let to = set.upperBound(50);
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     *
+     * let set = new TreeMultiSet();
+     * ... // add key-value pairs., using numbers as keys
+     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
+     * let from = new ReverseIterator(set.upperBound(50));
+     * let to = new ReverseIterator(set.lowerBound(0));
+     * let it = from;
+     * while (!it.equals(to)) {
+     *   console.log(it.key);
+     *   it.next();
+     * }
+     */
+    upperBound(key) {
+        return this.__t.upperBound(key);
+    }
+
+    /**
+     * @returns first element of the container, or undefined if container is empty
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * let first = set.first(); // 1
+     */
+    first() {
+        return this.__t.first();
+    }
+
+    /**
+     * @returns last element of the container, or undefined if container is empty
+     * @example
+     * let set = new TreeMultiSet([1, 2, 3]);
+     * let last = set.last(); // 3
+     */
+    last() {
+        return this.__t.last();
+    }
+
+    /**
+     * Serializes contents of the set in the form {key1,key2,...}
+     * @returns {String}
+     */
+    toString() {
+        return this.__t.toString();
+    }
+}
+
+module.exports = {
+    TreeMultiSet: TreeMultiSet,
+};
+
+/***/ })
+/******/ ]);
+});
+
+/***/ }),
+
 /***/ 742:
 /***/ (function(module, __unusedexports, __webpack_require__) {
 
diff --git a/package-lock.json b/package-lock.json
index 672aeff..121b6fc 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4435,6 +4435,11 @@
         "verror": "1.10.0"
       }
     },
+    "jstreemap": {
+      "version": "1.28.2",
+      "resolved": "https://registry.npmjs.org/jstreemap/-/jstreemap-1.28.2.tgz",
+      "integrity": "sha512-JYVDYwLat+OVXLpIXgUPy06wPSEkQafGOeotpGFodkM5+K8x3IrVPGaTw+Fd9MmZy092cLG/N+3q90oSBHZoQQ=="
+    },
     "jsx-ast-utils": {
       "version": "2.2.3",
       "resolved": "https://registry.npmjs.org/jsx-ast-utils/-/jsx-ast-utils-2.2.3.tgz",
diff --git a/package.json b/package.json
index a3b4877..b8c4043 100644
--- a/package.json
+++ b/package.json
@@ -27,7 +27,8 @@
   "license": "MIT",
   "dependencies": {
     "@actions/core": "^1.2.2",
-    "@actions/github": "^2.1.0"
+    "@actions/github": "^2.1.0",
+    "jstreemap": "^1.28.2"
   },
   "devDependencies": {
     "@types/jest": "^24.0.23",
diff --git a/src/main.ts b/src/main.ts
index 9ae7083..560f836 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,5 +1,38 @@
 import * as github from '@actions/github'
 import * as core from '@actions/core'
+import Octokit from '@octokit/rest'
+import * as treemap from 'jstreemap'
+
+function createRunsQuery(
+  octokit: github.GitHub,
+  owner: string,
+  repo: string,
+  workflowId: string,
+  status: string,
+  branch?: string,
+  event?: string
+): Octokit.RequestOptions {
+  const request =
+    branch === undefined
+      ? {
+          owner,
+          repo,
+          // eslint-disable-next-line @typescript-eslint/camelcase
+          workflow_id: workflowId,
+          status
+        }
+      : {
+          owner,
+          repo,
+          // eslint-disable-next-line @typescript-eslint/camelcase
+          workflow_id: workflowId,
+          status,
+          branch,
+          event
+        }
+
+  return octokit.actions.listWorkflowRuns.endpoint.merge(request)
+}
 
 async function cancelDuplicates(
   token: string,
@@ -13,7 +46,7 @@ async function cancelDuplicates(
   const octokit = new github.GitHub(token)
 
   // Deteermind the workflow to reduce the result set, or reference anothre workflow
-  let resolvedId
+  let resolvedId = ''
   if (workflowId === undefined) {
     const reply = await octokit.actions.getWorkflowRun({
       owner,
@@ -22,73 +55,75 @@ async function cancelDuplicates(
       run_id: Number.parseInt(selfRunId)
     })
 
-    resolvedId = reply.data.workflow_url.split('/').pop()
+    resolvedId = reply.data.workflow_url.split('/').pop() || ''
+    if (!(resolvedId.length > 0)) {
+      throw new Error('Could not resolve workflow')
+    }
   } else {
     resolvedId = workflowId
   }
 
   core.info(`Workflow ID is: ${resolvedId}`)
 
-  const request =
-    branch === undefined
-      ? {
-          owner,
-          repo,
-          // eslint-disable-next-line @typescript-eslint/camelcase
-          workflow_id: resolvedId
-        }
-      : {
-          owner,
-          repo,
-          // eslint-disable-next-line @typescript-eslint/camelcase
-          workflow_id: resolvedId,
-          branch,
-          event
-        }
-
-  const listRuns = octokit.actions.listWorkflowRuns.endpoint.merge(request)
+  // eslint-disable-next-line @typescript-eslint/no-explicit-any
+  const sorted = new treemap.TreeMap<number, any>()
+  for (const status of ['queued', 'in_progress']) {
+    const listRuns = createRunsQuery(
+      octokit,
+      owner,
+      repo,
+      resolvedId,
+      status,
+      branch,
+      event
+    )
+    for await (const item of octokit.paginate.iterator(listRuns)) {
+      // There is some sort of bug where the pagination URLs point to a
+      // different endpoint URL which trips up the resulting representation
+      // In that case, fallback to the actual REST 'workflow_runs' property
+      const elements =
+        item.data.length === undefined ? item.data.workflow_runs : item.data
+
+      for (const element of elements) {
+        sorted.set(element.run_number, element)
+      }
+    }
+  }
 
   // If a workflow was provided process everything
   let matched = workflowId !== undefined
   const heads = new Set()
-  for await (const item of octokit.paginate.iterator(listRuns)) {
-    // There is some sort of bug where the pagination URLs point to a
-    // different endpoint URL which trips up the resulting representation
-    // In that case, fallback to the actual REST 'workflow_runs' property
-    const elements =
-      item.data.length === undefined ? item.data.workflow_runs : item.data
-
-    for (const element of elements) {
-      core.info(
-        `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
-      )
-
-      if (!matched) {
-        if (element.id.toString() !== selfRunId) {
-          // Skip everything up to this run
-          continue
-        }
-
-        matched = true
-        core.info(`Matched ${selfRunId}`)
-      }
+  for (const entry of sorted.backward()) {
+    const element = entry[1]
+    core.info(
+      `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
+    )
 
-      if ('completed' === element.status.toString()) {
+    if (!matched) {
+      if (element.id.toString() !== selfRunId) {
+        // Skip everything up to this run
         continue
       }
 
-      // This is a set of one in the non-schedule case, otherwise everything is a candidate
-      const head = `${element.head_repository.full_name}/${element.head_branch}`
-      if (!heads.has(head)) {
-        core.info(`First: ${head}`)
-        heads.add(head)
-        continue
-      }
+      matched = true
+      core.info(`Matched ${selfRunId}`)
+    }
 
-      core.info(`Cancelling: ${head}`)
+    if ('completed' === element.status.toString()) {
+      continue
+    }
 
-      await cancelRun(octokit, owner, repo, element.id)
+    // This is a set of one in the non-schedule case, otherwise everything is a candidate
+    const head = `${element.head_repository.full_name}/${element.head_branch}`
+    if (!heads.has(head)) {
+      core.info(`First: ${head}`)
+      heads.add(head)
+      continue
     }
+
+    core.info(`Cancelling: ${head}`)
+
+    await cancelRun(octokit, owner, repo, element.id)
   }
 }
 


[airflow-cancel-workflow-runs] 20/44: Bump acorn from 5.7.3 to 5.7.4

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit cc3b6f2c35013ba77421c235280204cf5aec7415
Author: dependabot[bot] <49...@users.noreply.github.com>
AuthorDate: Mon Mar 16 01:53:34 2020 +0000

    Bump acorn from 5.7.3 to 5.7.4
    
    Bumps [acorn](https://github.com/acornjs/acorn) from 5.7.3 to 5.7.4.
    - [Release notes](https://github.com/acornjs/acorn/releases)
    - [Commits](https://github.com/acornjs/acorn/compare/5.7.3...5.7.4)
    
    Signed-off-by: dependabot[bot] <su...@github.com>
---
 package-lock.json | 12 ++++++------
 1 file changed, 6 insertions(+), 6 deletions(-)

diff --git a/package-lock.json b/package-lock.json
index 121b6fc..5aa22c3 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -786,9 +786,9 @@
       "dev": true
     },
     "acorn": {
-      "version": "6.4.0",
-      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.0.tgz",
-      "integrity": "sha512-gac8OEcQ2Li1dxIEWGZzsp2BitJxwkwcOm0zHAJLcPJaVvm58FRnk6RkuLRpU1EujipU2ZFODv2P9DLMfnV8mw==",
+      "version": "6.4.1",
+      "resolved": "https://registry.npmjs.org/acorn/-/acorn-6.4.1.tgz",
+      "integrity": "sha512-ZVA9k326Nwrj3Cj9jlh3wGFutC2ZornPNARZwsNYqQYgN0EsV2d53w5RN/co65Ohn4sUAUtb1rSUAOD6XN9idA==",
       "dev": true
     },
     "acorn-globals": {
@@ -4363,9 +4363,9 @@
       },
       "dependencies": {
         "acorn": {
-          "version": "5.7.3",
-          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.3.tgz",
-          "integrity": "sha512-T/zvzYRfbVojPWahDsE5evJdHb3oJoQfFbsrKM7w5Zcs++Tr257tia3BmMP8XYVjp1S9RZXQMh7gao96BlqZOw==",
+          "version": "5.7.4",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-5.7.4.tgz",
+          "integrity": "sha512-1D++VG7BhrtvQpNbBzovKNc1FLGGEE/oGe7b9xJm/RFHMBeUaUGpluV9RLjZa47YFdPcDAenEYuq9pQPcMdLJg==",
           "dev": true
         }
       }


[airflow-cancel-workflow-runs] 26/44: Adds cancelling of latest job when certain jobs failed

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f0bf36ae0bb733d9cdd659316aaf3d2003b09366
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Jul 25 20:57:09 2020 +0200

    Adds cancelling of latest job when certain jobs failed
---
 .gitignore    |   4 +-
 README.md     |  82 ++++++--
 action.yml    |  14 +-
 dist/index.js | 635 ++++++++++++++++++++++++++++++++++++----------------------
 src/main.ts   | 422 ++++++++++++++++++++++++--------------
 5 files changed, 745 insertions(+), 412 deletions(-)

diff --git a/.gitignore b/.gitignore
index 18e337d..0a04613 100644
--- a/.gitignore
+++ b/.gitignore
@@ -96,4 +96,6 @@ Thumbs.db
 
 # Ignore built ts files
 __tests__/runner/*
-lib/**/*
\ No newline at end of file
+lib/**/*
+
+.idea
diff --git a/README.md b/README.md
index 1448802..5c77714 100644
--- a/README.md
+++ b/README.md
@@ -1,24 +1,79 @@
-# cancel-previous-runs 
-This action cancels previous runs for one or more branches/prs associated with a workflow, effectively limiting the resource consumption of the workflow to one per branch.
+# cancel-workflow-runs
+This action cancels runs for one or more branches/prs associated with a workflow,
+effectively limiting the resource consumption of the workflow to one per branch.
 
-<p><a href="https://github.com/actions/typescript-action/actions"><img alt="typescript-action status" src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
+It also cancels workflows from the latest workflow run if specified jobs failed.
+That allows to further limit the resource usage of running workflows, without
+impacting the elapsed time of successful workflow runs. Typical behaviour of
+the Github Actions Workflow is that the success/failure propagation between the jobs
+happens through job dependency graph (needs: in the GA yaml). However, there are cases
+where you want to start some jobs without waiting for other jobs to succeed, yet if
+the other jobs fail, you want to cancel the whole workflow. It's similar to
+fail-fast behaviour of the matrix builds.
+
+Since cancelling workflow does not work from "fork" pull requests for security reasons,
+the capability of canceling the workflows should be built in the scheduled task.
+
+<p><a href="https://github.com/actions/typescript-action/actions">
+<img alt="typescript-action status"
+    src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
+
+I based the implementation of this action on the
+[n1hility action](https://github.com/n1hility/cancel-previous-runs) to cancel the previous runs only.
 
 ## Usage
 
-The easiest and most complete approach to utilize this action, is to create a separate schedule event triggered workflow, which is directed at the workflow you wish to clear duplicate runs. At each cron interval all branches and all PRs executing for either push or pull_request events will be processed and limited to one run per branch/pr.
+The easiest and most complete approach to utilize this action, is to create a separate schedule event
+triggered workflow, which is directed at the workflow you wish to clear duplicate runs.
+At each cron interval all branches and all PRs executing for either push or pull_request events
+will be processed and limited to one run per branch/pr.
 
-Additionally this action can be placed as an early step in your workflow (e.g. after checkout), so that it can abort the other previously running jobs immediately, in case most resources are tied up. Unfortunately this approach is a no-op when a pull request uses a fork for a source branch. This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only permissions for security reasons. write permissions are required to be able to cancel a job. Therefore,  [...]
+Additionally, this action can be placed as an early step in your workflow (e.g. after the checkout), so
+that it can abort the other previously running jobs immediately, in case the workflows tie up most resources.
+Unfortunately this approach is a no-op when a pull request uses a fork for a source branch.
+This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only
+permissions for security reasons. You need write permissions to be able to cancel a job.
+Therefore, it's a good idea to only rely on this approach as a fallback in-addition to the previously
+described scheduling model.
 
 ### Inputs
 
-token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible in the repository, **DO NOT HARDCODE A TOKEN ONLY USE A REFERENCE**. 
-workflow - The filename of the workflow to limit runs on (only applies to schedule events) 
-
+* token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible
+  in the repository, **DO NOT HARDCODE A TOKEN ONLY USE A REFERENCE**.
+* workflow - The filename of the workflow to limit runs on (only applies to schedule events)
+* failFastJobNames - optional array of job name regexps. If a job name that matches any of the regexp fails
+  in the most recent run, this causes a fail-fast of the run. This can be used if you want to run jobs
+  in parallel but kill them as soon as some of those jobs fail - effectively turning them into "fail-fast"
+  type of jobs. Note these are job names after interpolation of workflow variables - so you have to make sure that
+  you use the name as displayed in the status of the workflow or use regexp to
+  match the names.
 
 ### Schedule Example
 
 ```yaml
-name: Cleanup Duplicate Branches and PRs  
+name: Cleanup Duplicate Branches and PRs
+on:
+  schedule:
+    - cron:  '*/15 * * * *'
+cancel-runs:
+  # Prevent forks from running this to be nice
+  if: github.repository == 'foo-org/my-repo'
+  runs-on: ubuntu-latest
+    steps:
+      - uses: n1hility/cancel-previous-runs@v2
+        with:
+          token: ${{ secrets.GITHUB_TOKEN }}
+          workflow: my-heavy-workflow.yml
+```
+
+
+### Schedule Example with fail-fast
+
+This kills all previous runs of the workflow, and also latest run if one of the jobs
+matching `^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp failed in it.
+
+```yaml
+name: Cleanup Duplicate Branches and fail-fast errors
 on:
   schedule:
     - cron:  '*/15 * * * *'
@@ -28,23 +83,24 @@ cancel-runs:
   runs-on: ubuntu-latest
     steps:
       - uses: n1hility/cancel-previous-runs@v2
-        with: 
+        with:
           token: ${{ secrets.GITHUB_TOKEN }}
           workflow: my-heavy-workflow.yml
+          failFastJobNames: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
 ```
 
 
 ### Alternate/Fallback Example
 
 ```yaml
-  test: 
+  test:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v1
     - uses: n1hility/cancel-previous-runs@v2
-      with: 
+      with:
         token: ${{ secrets.GITHUB_TOKEN }}
 ```
 
 ## License
-The scripts and documentation in this project are released under the [MIT License](LICENSE)
+[MIT License](LICENSE) covers the scripts and documentation in this project.
diff --git a/action.yml b/action.yml
index e0d9c6d..1b2515d 100644
--- a/action.yml
+++ b/action.yml
@@ -1,12 +1,16 @@
-name: 'Cancel Previous Workflow Runs'
-description: 'Cancels all previous runs of this workflow'
-author: 'n1hility'
+name: 'Cancel Workflow Runs'
+description: 'Cancels previous runs and optionally failed runs of a workflow'
+author: 'potiuk'
 inputs:
   token:
-    description: The GITHUB_TOKEN secret of this github workflow
+    description: The GITHUB_TOKEN secret of the repository
     required: true
   workflow:
-    description: The filename of the workflow to limit runs on (only applies to schedule events)
+    description: The filename of the workflow to limit runs on (only applies to scheduled events)
+    required: false
+  failFastJobNames:
+    description: |
+      Array of job names (JSON-encoded string). Failures of those jobs are fail-fast of whole workflow.
     required: false
 runs:
   using: 'node12'
diff --git a/dist/index.js b/dist/index.js
index 5505475..302be03 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -34,7 +34,7 @@ module.exports =
 /******/ 	// the startup function
 /******/ 	function startup() {
 /******/ 		// Load entry module and return exports
-/******/ 		return __webpack_require__(131);
+/******/ 		return __webpack_require__(198);
 /******/ 	};
 /******/
 /******/ 	// run startup
@@ -1434,208 +1434,6 @@ module.exports = require("child_process");
 
 /***/ }),
 
-/***/ 131:
-/***/ (function(__unusedmodule, exports, __webpack_require__) {
-
-"use strict";
-
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-};
-var __asyncValues = (this && this.__asyncValues) || function (o) {
-    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
-    var m = o[Symbol.asyncIterator], i;
-    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
-    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
-    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
-};
-var __importStar = (this && this.__importStar) || function (mod) {
-    if (mod && mod.__esModule) return mod;
-    var result = {};
-    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
-    result["default"] = mod;
-    return result;
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-const github = __importStar(__webpack_require__(469));
-const core = __importStar(__webpack_require__(393));
-const treemap = __importStar(__webpack_require__(706));
-function createRunsQuery(octokit, owner, repo, workflowId, status, branch, event) {
-    const request = branch === undefined
-        ? {
-            owner,
-            repo,
-            // eslint-disable-next-line @typescript-eslint/camelcase
-            workflow_id: workflowId,
-            status
-        }
-        : {
-            owner,
-            repo,
-            // eslint-disable-next-line @typescript-eslint/camelcase
-            workflow_id: workflowId,
-            status,
-            branch,
-            event
-        };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
-}
-function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, event) {
-    var e_1, _a;
-    return __awaiter(this, void 0, void 0, function* () {
-        const octokit = new github.GitHub(token);
-        // Deteermind the workflow to reduce the result set, or reference anothre workflow
-        let resolvedId = '';
-        if (workflowId === undefined) {
-            const reply = yield octokit.actions.getWorkflowRun({
-                owner,
-                repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                run_id: Number.parseInt(selfRunId)
-            });
-            resolvedId = reply.data.workflow_url.split('/').pop() || '';
-            if (!(resolvedId.length > 0)) {
-                throw new Error('Could not resolve workflow');
-            }
-        }
-        else {
-            resolvedId = workflowId;
-        }
-        core.info(`Workflow ID is: ${resolvedId}`);
-        // eslint-disable-next-line @typescript-eslint/no-explicit-any
-        const sorted = new treemap.TreeMap();
-        for (const status of ['queued', 'in_progress']) {
-            const listRuns = createRunsQuery(octokit, owner, repo, resolvedId, status, branch, event);
-            try {
-                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
-                    const item = _c.value;
-                    // There is some sort of bug where the pagination URLs point to a
-                    // different endpoint URL which trips up the resulting representation
-                    // In that case, fallback to the actual REST 'workflow_runs' property
-                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
-                    for (const element of elements) {
-                        sorted.set(element.run_number, element);
-                    }
-                }
-            }
-            catch (e_1_1) { e_1 = { error: e_1_1 }; }
-            finally {
-                try {
-                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
-                }
-                finally { if (e_1) throw e_1.error; }
-            }
-        }
-        // If a workflow was provided process everything
-        let matched = workflowId !== undefined;
-        const heads = new Set();
-        for (const entry of sorted.backward()) {
-            const element = entry[1];
-            core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
-            if (!matched) {
-                if (element.id.toString() !== selfRunId) {
-                    // Skip everything up to this run
-                    continue;
-                }
-                matched = true;
-                core.info(`Matched ${selfRunId}`);
-            }
-            if ('completed' === element.status.toString() ||
-                !['push', 'pull_request'].includes(element.event.toString())) {
-                continue;
-            }
-            // This is a set of one in the non-schedule case, otherwise everything is a candidate
-            const head = `${element.head_repository.full_name}/${element.head_branch}`;
-            if (!heads.has(head)) {
-                core.info(`First: ${head}`);
-                heads.add(head);
-                continue;
-            }
-            core.info(`Cancelling: ${head}`);
-            yield cancelRun(octokit, owner, repo, element.id);
-        }
-    });
-}
-function run() {
-    return __awaiter(this, void 0, void 0, function* () {
-        try {
-            const token = core.getInput('token');
-            core.info(token);
-            const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
-            const repository = getRequiredEnv('GITHUB_REPOSITORY');
-            const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
-            const [owner, repo] = repository.split('/');
-            const branchPrefix = 'refs/heads/';
-            const tagPrefix = 'refs/tags/';
-            if ('schedule' === eventName) {
-                const workflowId = core.getInput('workflow');
-                if (!(workflowId.length > 0)) {
-                    throw new Error('Workflow must be specified for schedule event type');
-                }
-                yield cancelDuplicates(token, selfRunId, owner, repo, workflowId);
-                return;
-            }
-            if (!['push', 'pull_request'].includes(eventName)) {
-                core.info('Skipping unsupported event');
-                return;
-            }
-            const pullRequest = 'pull_request' === eventName;
-            let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF');
-            if (!pullRequest && !branch.startsWith(branchPrefix)) {
-                if (branch.startsWith(tagPrefix)) {
-                    core.info(`Skipping tag build`);
-                    return;
-                }
-                const message = `${branch} was not an expected branch ref (refs/heads/).`;
-                throw new Error(message);
-            }
-            branch = branch.replace(branchPrefix, '');
-            core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
-            cancelDuplicates(token, selfRunId, owner, repo, undefined, branch, eventName);
-        }
-        catch (error) {
-            core.setFailed(error.message);
-        }
-    });
-}
-function cancelRun(
-// eslint-disable-next-line @typescript-eslint/no-explicit-any
-octokit, owner, repo, id) {
-    return __awaiter(this, void 0, void 0, function* () {
-        let reply;
-        try {
-            reply = yield octokit.actions.cancelWorkflowRun({
-                owner,
-                repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                run_id: id
-            });
-            core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`);
-        }
-        catch (error) {
-            core.info(`[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`);
-        }
-    });
-}
-function getRequiredEnv(key) {
-    const value = process.env[key];
-    if (value === undefined) {
-        const message = `${key} was not defined.`;
-        throw new Error(message);
-    }
-    return value;
-}
-run();
-
-
-/***/ }),
-
 /***/ 141:
 /***/ (function(__unusedmodule, exports, __webpack_require__) {
 
@@ -2190,6 +1988,311 @@ function checkMode (stat, options) {
 
 /***/ }),
 
+/***/ 198:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __asyncValues = (this && this.__asyncValues) || function (o) {
+    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
+    var m = o[Symbol.asyncIterator], i;
+    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
+    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
+    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const github = __importStar(__webpack_require__(469));
+const core = __importStar(__webpack_require__(393));
+const treemap = __importStar(__webpack_require__(706));
+function createListRunsQueryForAllRuns(octokit, owner, repo, workflowId, status) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        workflow_id: workflowId,
+        status
+    };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
+function createListRunsQueryForSelfRun(octokit, owner, repo, workflowId, status, branch, eventName) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        workflow_id: workflowId,
+        status,
+        branch,
+        event: eventName
+    };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
+function createJobsForWorkflowRunQuery(octokit, owner, repo, runId) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        run_id: runId,
+    };
+    return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request);
+}
+function cancelOnFailFastJobsFailed(octokit, owner, repo, runId, head, failFastJobNames) {
+    var e_1, _a;
+    return __awaiter(this, void 0, void 0, function* () {
+        const listJobs = createJobsForWorkflowRunQuery(octokit, owner, repo, runId);
+        core.info(`Cancelling runId ${runId} in case one of the ${failFastJobNames} failed`);
+        try {
+            for (var _b = __asyncValues(octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
+                const item = _c.value;
+                for (const job of item.data.jobs) {
+                    core.info(`The job name: ${job.name}, Conclusion: ${job.conclusion}`);
+                    if (job.conclusion == 'failure' &&
+                        failFastJobNames.some(jobNameRegexp => job.name.match(jobNameRegexp))) {
+                        core.info(`Job ${job.name} has failed and it matches one of the ${failFastJobNames} regexps`);
+                        core.info(`Cancelling the workflow run: ${runId}, head: ${head}`);
+                        yield cancelRun(octokit, owner, repo, runId);
+                        return;
+                    }
+                }
+            }
+        }
+        catch (e_1_1) { e_1 = { error: e_1_1 }; }
+        finally {
+            try {
+                if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+            }
+            finally { if (e_1) throw e_1.error; }
+        }
+    });
+}
+function getSelfWorkflowId(octokit, selfRunId, owner, repo) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let workflowId;
+        const reply = yield octokit.actions.getWorkflowRun({
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            run_id: Number.parseInt(selfRunId)
+        });
+        workflowId = reply.data.workflow_url.split('/').pop() || '';
+        if (!(workflowId.length > 0)) {
+            throw new Error('Could not resolve workflow');
+        }
+        return workflowId;
+    });
+}
+function getSortedWorkflowRuns(octokit, createListRunQuery) {
+    var e_2, _a;
+    return __awaiter(this, void 0, void 0, function* () {
+        const sortedWorkflowRuns = new treemap.TreeMap();
+        for (const status of ['queued', 'in_progress']) {
+            const listRuns = yield createListRunQuery(status);
+            try {
+                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                    const item = _c.value;
+                    // There is some sort of bug where the pagination URLs point to a
+                    // different endpoint URL which trips up the resulting representation
+                    // In that case, fallback to the actual REST 'workflow_runs' property
+                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
+                    for (const element of elements) {
+                        sortedWorkflowRuns.set(element.run_number, element);
+                    }
+                }
+            }
+            catch (e_2_1) { e_2 = { error: e_2_1 }; }
+            finally {
+                try {
+                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+                }
+                finally { if (e_2) throw e_2.error; }
+            }
+        }
+        core.info(`Found runs: ${Array.from(sortedWorkflowRuns.backward()).map(t => t[0])}`);
+        return sortedWorkflowRuns;
+    });
+}
+function shouldRunBeSkipped(runItem) {
+    if ('completed' === runItem.status.toString()) {
+        core.info(`Skip completed run: ${runItem.id}`);
+        return true;
+    }
+    if (!['push', 'pull_request'].includes(runItem.event.toString())) {
+        core.info(`Skip run: ${runItem.id} as it is neither push nor pull_request (${runItem.event}`);
+        return true;
+    }
+    return false;
+}
+function cancelRun(octokit, owner, repo, id) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let reply;
+        try {
+            reply = yield octokit.actions.cancelWorkflowRun({
+                owner: owner,
+                repo: repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                run_id: id
+            });
+            core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`);
+        }
+        catch (error) {
+            core.info(`[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`);
+        }
+    });
+}
+// Kills past runs for my own workflow.
+function findAndCancelPastRunsForSelf(octokit, selfRunId, owner, repo, branch, eventName) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info(`findAndCancelPastRunsForSelf:  ${selfRunId}, ${owner}, ${repo}, ${branch}, ${eventName}`);
+        const workflowId = yield getSelfWorkflowId(octokit, selfRunId, owner, repo);
+        core.info(`My own workflow ID is: ${workflowId}`);
+        const sortedWorkflowRuns = yield getSortedWorkflowRuns(octokit, function (status) {
+            return createListRunsQueryForSelfRun(octokit, owner, repo, workflowId, status, branch, eventName);
+        });
+        let matched = false;
+        const headsToRunIdMap = new Map();
+        for (const [key, runItem] of sortedWorkflowRuns.backward()) {
+            core.info(`Run number: ${key}, RunId: ${runItem.id}, URL: ${runItem.workflow_url}. Status ${runItem.status}`);
+            if (!matched) {
+                if (runItem.id.toString() !== selfRunId) {
+                    core.info(`Skip run ${runItem.id} as it was started before my own id: ${selfRunId}`);
+                    continue;
+                }
+                matched = true;
+                core.info(`Matched ${selfRunId}. Reached my own ID, now looping through all remaining runs/`);
+                core.info("I will cancel all except the first for each 'head' available");
+            }
+            if (shouldRunBeSkipped(runItem)) {
+                continue;
+            }
+            // Head of the run
+            const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`;
+            if (!headsToRunIdMap.has(head)) {
+                core.info(`First run for the head: ${head}. Skipping it. Next ones with same head will be cancelled.`);
+                headsToRunIdMap.set(head, runItem.id);
+                continue;
+            }
+            core.info(`Cancelling run: ${runItem.id}, head ${head}.`);
+            core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`);
+            yield cancelRun(octokit, owner, repo, runItem.id);
+        }
+    });
+}
+// Kills past runs for my own workflow.
+function findAndCancelPastRunsForSchedule(octokit, workflowId, owner, repo, failFastJobNames) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info(`findAndCancelPastRunsForSchedule: ${owner}, ${workflowId}, ${repo}`);
+        const sortedWorkflowRuns = yield getSortedWorkflowRuns(octokit, function (status) {
+            return createListRunsQueryForAllRuns(octokit, owner, repo, workflowId, status);
+        });
+        const headsToRunIdMap = new Map();
+        for (const [key, runItem] of sortedWorkflowRuns.backward()) {
+            core.info(` ${key} ${runItem.id} : ${runItem.workflow_url} : ${runItem.status} : ${runItem.run_number}`);
+            if (shouldRunBeSkipped(runItem)) {
+                continue;
+            }
+            // Head of the run
+            const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`;
+            if (!headsToRunIdMap.has(head)) {
+                core.info(`First run for the head: ${head}. Next runs with the same head will be cancelled.`);
+                headsToRunIdMap.set(head, runItem.id);
+                if (failFastJobNames !== undefined) {
+                    core.info("Checking if the head run failed in specified jobs");
+                    yield cancelOnFailFastJobsFailed(octokit, owner, repo, runItem.id, head, failFastJobNames);
+                }
+                else {
+                    core.info("Skipping the head run.");
+                }
+                continue;
+            }
+            core.info(`Cancelling run: ${runItem.id}, head ${head}.`);
+            core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`);
+            yield cancelRun(octokit, owner, repo, runItem.id);
+        }
+    });
+}
+function getRequiredEnv(key) {
+    const value = process.env[key];
+    if (value === undefined) {
+        const message = `${key} was not defined.`;
+        throw new Error(message);
+    }
+    return value;
+}
+function runScheduledRun(octokit, owner, repo) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const workflowId = core.getInput('workflow');
+        if (!(workflowId.length > 0)) {
+            core.setFailed('Workflow must be specified for schedule event type');
+            return;
+        }
+        const failFastJobNames = JSON.parse(core.getInput('failFastJobNames'));
+        if (failFastJobNames !== undefined) {
+            core.info(`Checking also if last run failed in one of the jobs: ${failFastJobNames}`);
+        }
+        yield findAndCancelPastRunsForSchedule(octokit, workflowId, owner, repo, failFastJobNames);
+        return;
+    });
+}
+function runRegularRun(octokit, selfRunId, owner, repo, eventName) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const pullRequest = 'pull_request' === eventName;
+        const branchPrefix = 'refs/heads/';
+        const tagPrefix = 'refs/tags/';
+        let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF');
+        if (!pullRequest && !branch.startsWith(branchPrefix)) {
+            if (branch.startsWith(tagPrefix)) {
+                core.info(`Skipping tag build`);
+                return;
+            }
+            core.setFailed(`${branch} was not an expected branch ref (refs/heads/).`);
+            return;
+        }
+        branch = branch.replace(branchPrefix, '');
+        core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
+        yield findAndCancelPastRunsForSelf(octokit, selfRunId, owner, repo, branch, eventName);
+    });
+}
+function run() {
+    return __awaiter(this, void 0, void 0, function* () {
+        const token = core.getInput('token');
+        const octokit = new github.GitHub(token);
+        core.info(`Starting checking for workflows to cancel`);
+        const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
+        const repository = getRequiredEnv('GITHUB_REPOSITORY');
+        const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
+        const [owner, repo] = repository.split('/');
+        if ('schedule' === eventName) {
+            yield runScheduledRun(octokit, owner, repo);
+        }
+        else if (!['push', 'pull_request'].includes(eventName)) {
+            core.info('Skipping unsupported event');
+            return;
+        }
+        else {
+            yield runRegularRun(octokit, selfRunId, owner, repo, eventName);
+        }
+    });
+}
+run().then(() => core.info("Cancel complete")).catch(e => core.setFailed(e.message));
+
+
+/***/ }),
+
 /***/ 211:
 /***/ (function(module) {
 
@@ -2200,7 +2303,7 @@ module.exports = require("https");
 /***/ 215:
 /***/ (function(module) {
 
-module.exports = {"_args":[["@octokit/rest@16.43.0","/Users/jason/devel/typescript-action"]],"_from":"@octokit/rest@16.43.0","_id":"@octokit/rest@16.43.0","_inBundle":false,"_integrity":"sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==","_location":"/@octokit/rest","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.0","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSp [...]
+module.exports = {"_args":[["@octokit/rest@16.43.0","/home/jarek/code/cancel-previous-runs"]],"_from":"@octokit/rest@16.43.0","_id":"@octokit/rest@16.43.0","_inBundle":false,"_integrity":"sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==","_location":"/@octokit/rest","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.0","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawS [...]
 
 /***/ }),
 
@@ -7692,12 +7795,22 @@ var HttpCodes;
     HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
     HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
     HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
+    HttpCodes[HttpCodes["TooManyRequests"] = 429] = "TooManyRequests";
     HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
     HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
     HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
     HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
     HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
 })(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
+var Headers;
+(function (Headers) {
+    Headers["Accept"] = "accept";
+    Headers["ContentType"] = "content-type";
+})(Headers = exports.Headers || (exports.Headers = {}));
+var MediaTypes;
+(function (MediaTypes) {
+    MediaTypes["ApplicationJson"] = "application/json";
+})(MediaTypes = exports.MediaTypes || (exports.MediaTypes = {}));
 /**
  * Returns the proxy URL, depending upon the supplied url and proxy environment variables.
  * @param serverUrl  The server URL where the request will be sent. For example, https://api.github.com
@@ -7707,8 +7820,18 @@ function getProxyUrl(serverUrl) {
     return proxyUrl ? proxyUrl.href : '';
 }
 exports.getProxyUrl = getProxyUrl;
-const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect];
-const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout];
+const HttpRedirectCodes = [
+    HttpCodes.MovedPermanently,
+    HttpCodes.ResourceMoved,
+    HttpCodes.SeeOther,
+    HttpCodes.TemporaryRedirect,
+    HttpCodes.PermanentRedirect
+];
+const HttpResponseRetryCodes = [
+    HttpCodes.BadGateway,
+    HttpCodes.ServiceUnavailable,
+    HttpCodes.GatewayTimeout
+];
 const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
 const ExponentialBackoffCeiling = 10;
 const ExponentialBackoffTimeSlice = 5;
@@ -7800,22 +7923,29 @@ class HttpClient {
      * Gets a typed object from an endpoint
      * Be aware that not found returns a null.  Other errors (4xx, 5xx) reject the promise
      */
-    async getJson(requestUrl, additionalHeaders) {
+    async getJson(requestUrl, additionalHeaders = {}) {
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
         let res = await this.get(requestUrl, additionalHeaders);
         return this._processResponse(res, this.requestOptions);
     }
-    async postJson(requestUrl, obj, additionalHeaders) {
+    async postJson(requestUrl, obj, additionalHeaders = {}) {
         let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
         let res = await this.post(requestUrl, data, additionalHeaders);
         return this._processResponse(res, this.requestOptions);
     }
-    async putJson(requestUrl, obj, additionalHeaders) {
+    async putJson(requestUrl, obj, additionalHeaders = {}) {
         let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
         let res = await this.put(requestUrl, data, additionalHeaders);
         return this._processResponse(res, this.requestOptions);
     }
-    async patchJson(requestUrl, obj, additionalHeaders) {
+    async patchJson(requestUrl, obj, additionalHeaders = {}) {
         let data = JSON.stringify(obj, null, 2);
+        additionalHeaders[Headers.Accept] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.Accept, MediaTypes.ApplicationJson);
+        additionalHeaders[Headers.ContentType] = this._getExistingOrDefaultHeader(additionalHeaders, Headers.ContentType, MediaTypes.ApplicationJson);
         let res = await this.patch(requestUrl, data, additionalHeaders);
         return this._processResponse(res, this.requestOptions);
     }
@@ -7826,18 +7956,22 @@ class HttpClient {
      */
     async request(verb, requestUrl, data, headers) {
         if (this._disposed) {
-            throw new Error("Client has already been disposed.");
+            throw new Error('Client has already been disposed.');
         }
         let parsedUrl = url.parse(requestUrl);
         let info = this._prepareRequest(verb, parsedUrl, headers);
         // Only perform retries on reads since writes may not be idempotent.
-        let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1;
+        let maxTries = this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1
+            ? this._maxRetries + 1
+            : 1;
         let numTries = 0;
         let response;
         while (numTries < maxTries) {
             response = await this.requestRaw(info, data);
             // Check if it's an authentication challenge
-            if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) {
+            if (response &&
+                response.message &&
+                response.message.statusCode === HttpCodes.Unauthorized) {
                 let authenticationHandler;
                 for (let i = 0; i < this.handlers.length; i++) {
                     if (this.handlers[i].canHandleAuthentication(response)) {
@@ -7855,21 +7989,32 @@ class HttpClient {
                 }
             }
             let redirectsRemaining = this._maxRedirects;
-            while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1
-                && this._allowRedirects
-                && redirectsRemaining > 0) {
-                const redirectUrl = response.message.headers["location"];
+            while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1 &&
+                this._allowRedirects &&
+                redirectsRemaining > 0) {
+                const redirectUrl = response.message.headers['location'];
                 if (!redirectUrl) {
                     // if there's no location to redirect to, we won't
                     break;
                 }
                 let parsedRedirectUrl = url.parse(redirectUrl);
-                if (parsedUrl.protocol == 'https:' && parsedUrl.protocol != parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) {
-                    throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.");
+                if (parsedUrl.protocol == 'https:' &&
+                    parsedUrl.protocol != parsedRedirectUrl.protocol &&
+                    !this._allowRedirectDowngrade) {
+                    throw new Error('Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.');
                 }
                 // we need to finish reading the response before reassigning response
                 // which will leak the open socket.
                 await response.readBody();
+                // strip authorization header if redirected to a different hostname
+                if (parsedRedirectUrl.hostname !== parsedUrl.hostname) {
+                    for (let header in headers) {
+                        // header names are case insensitive
+                        if (header.toLowerCase() === 'authorization') {
+                            delete headers[header];
+                        }
+                    }
+                }
                 // let's make the request with the new redirectUrl
                 info = this._prepareRequest(verb, parsedRedirectUrl, headers);
                 response = await this.requestRaw(info, data);
@@ -7920,8 +8065,8 @@ class HttpClient {
      */
     requestRawWithCallback(info, data, onResult) {
         let socket;
-        if (typeof (data) === 'string') {
-            info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
+        if (typeof data === 'string') {
+            info.options.headers['Content-Length'] = Buffer.byteLength(data, 'utf8');
         }
         let callbackCalled = false;
         let handleResult = (err, res) => {
@@ -7934,7 +8079,7 @@ class HttpClient {
             let res = new HttpClientResponse(msg);
             handleResult(null, res);
         });
-        req.on('socket', (sock) => {
+        req.on('socket', sock => {
             socket = sock;
         });
         // If we ever get disconnected, we want the socket to timeout eventually
@@ -7949,10 +8094,10 @@ class HttpClient {
             // res should have headers
             handleResult(err, null);
         });
-        if (data && typeof (data) === 'string') {
+        if (data && typeof data === 'string') {
             req.write(data, 'utf8');
         }
-        if (data && typeof (data) !== 'string') {
+        if (data && typeof data !== 'string') {
             data.on('close', function () {
                 req.end();
             });
@@ -7979,29 +8124,40 @@ class HttpClient {
         const defaultPort = usingSsl ? 443 : 80;
         info.options = {};
         info.options.host = info.parsedUrl.hostname;
-        info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort;
-        info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
+        info.options.port = info.parsedUrl.port
+            ? parseInt(info.parsedUrl.port)
+            : defaultPort;
+        info.options.path =
+            (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
         info.options.method = method;
         info.options.headers = this._mergeHeaders(headers);
         if (this.userAgent != null) {
-            info.options.headers["user-agent"] = this.userAgent;
+            info.options.headers['user-agent'] = this.userAgent;
         }
         info.options.agent = this._getAgent(info.parsedUrl);
         // gives handlers an opportunity to participate
         if (this.handlers) {
-            this.handlers.forEach((handler) => {
+            this.handlers.forEach(handler => {
                 handler.prepareRequest(info.options);
             });
         }
         return info;
     }
     _mergeHeaders(headers) {
-        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
+        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
         if (this.requestOptions && this.requestOptions.headers) {
             return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
         }
         return lowercaseKeys(headers || {});
     }
+    _getExistingOrDefaultHeader(additionalHeaders, header, _default) {
+        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => ((c[k.toLowerCase()] = obj[k]), c), {});
+        let clientHeader;
+        if (this.requestOptions && this.requestOptions.headers) {
+            clientHeader = lowercaseKeys(this.requestOptions.headers)[header];
+        }
+        return additionalHeaders[header] || clientHeader || _default;
+    }
     _getAgent(parsedUrl) {
         let agent;
         let proxyUrl = pm.getProxyUrl(parsedUrl);
@@ -8033,7 +8189,7 @@ class HttpClient {
                     proxyAuth: proxyUrl.auth,
                     host: proxyUrl.hostname,
                     port: proxyUrl.port
-                },
+                }
             };
             let tunnelAgent;
             const overHttps = proxyUrl.protocol === 'https:';
@@ -8060,7 +8216,9 @@ class HttpClient {
             // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
             // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
             // we have to cast it to any and change it directly
-            agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false });
+            agent.options = Object.assign(agent.options || {}, {
+                rejectUnauthorized: false
+            });
         }
         return agent;
     }
@@ -8121,7 +8279,7 @@ class HttpClient {
                     msg = contents;
                 }
                 else {
-                    msg = "Failed request: (" + statusCode + ")";
+                    msg = 'Failed request: (' + statusCode + ')';
                 }
                 let err = new Error(msg);
                 // attach statusCode and body obj (if available) to the error object
@@ -28666,12 +28824,10 @@ function getProxyUrl(reqUrl) {
     }
     let proxyVar;
     if (usingSsl) {
-        proxyVar = process.env["https_proxy"] ||
-            process.env["HTTPS_PROXY"];
+        proxyVar = process.env['https_proxy'] || process.env['HTTPS_PROXY'];
     }
     else {
-        proxyVar = process.env["http_proxy"] ||
-            process.env["HTTP_PROXY"];
+        proxyVar = process.env['http_proxy'] || process.env['HTTP_PROXY'];
     }
     if (proxyVar) {
         proxyUrl = url.parse(proxyVar);
@@ -28683,7 +28839,7 @@ function checkBypass(reqUrl) {
     if (!reqUrl.hostname) {
         return false;
     }
-    let noProxy = process.env["no_proxy"] || process.env["NO_PROXY"] || '';
+    let noProxy = process.env['no_proxy'] || process.env['NO_PROXY'] || '';
     if (!noProxy) {
         return false;
     }
@@ -28704,7 +28860,10 @@ function checkBypass(reqUrl) {
         upperReqHosts.push(`${upperReqHosts[0]}:${reqPort}`);
     }
     // Compare request host against noproxy
-    for (let upperNoProxyItem of noProxy.split(',').map(x => x.trim().toUpperCase()).filter(x => x)) {
+    for (let upperNoProxyItem of noProxy
+        .split(',')
+        .map(x => x.trim().toUpperCase())
+        .filter(x => x)) {
         if (upperReqHosts.some(x => x === upperNoProxyItem)) {
             return true;
         }
diff --git a/src/main.ts b/src/main.ts
index 0fededf..1bd5e3f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -3,214 +3,260 @@ import * as core from '@actions/core'
 import Octokit from '@octokit/rest'
 import * as treemap from 'jstreemap'
 
-function createRunsQuery(
+function createListRunsQueryForAllRuns(
   octokit: github.GitHub,
   owner: string,
   repo: string,
   workflowId: string,
   status: string,
-  branch?: string,
-  event?: string
 ): Octokit.RequestOptions {
-  const request =
-    branch === undefined
-      ? {
-          owner,
-          repo,
-          // eslint-disable-next-line @typescript-eslint/camelcase
-          workflow_id: workflowId,
-          status
-        }
-      : {
-          owner,
-          repo,
-          // eslint-disable-next-line @typescript-eslint/camelcase
-          workflow_id: workflowId,
-          status,
-          branch,
-          event
-        }
+  const request = {
+    owner,
+    repo,
+    // eslint-disable-next-line @typescript-eslint/camelcase
+    workflow_id: workflowId,
+    status
+  }
+  return octokit.actions.listWorkflowRuns.endpoint.merge(request)
+}
 
+function createListRunsQueryForSelfRun(
+    octokit: github.GitHub,
+    owner: string,
+    repo: string,
+    workflowId: string,
+    status: string,
+    branch: string,
+    eventName: string
+): Octokit.RequestOptions {
+  const request = {
+    owner,
+    repo,
+    // eslint-disable-next-line @typescript-eslint/camelcase
+    workflow_id: workflowId,
+    status,
+    branch,
+    event: eventName
+  }
   return octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
 
-async function cancelDuplicates(
-  token: string,
-  selfRunId: string,
-  owner: string,
-  repo: string,
-  workflowId?: string,
-  branch?: string,
-  event?: string
-): Promise<void> {
-  const octokit = new github.GitHub(token)
+function createJobsForWorkflowRunQuery(
+    octokit: github.GitHub,
+    owner: string,
+    repo: string,
+    runId: number,
+): Octokit.RequestOptions {
+  const request = {
+    owner,
+    repo,
+    // eslint-disable-next-line @typescript-eslint/camelcase
+    run_id: runId,
+  }
+  return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request)
+}
 
-  // Determine the workflow to reduce the result set, or reference another workflow
-  let resolvedId = ''
-  if (workflowId === undefined) {
-    const reply = await octokit.actions.getWorkflowRun({
+async function cancelOnFailFastJobsFailed(
+    octokit: github.GitHub,
+    owner: string,
+    repo: string,
+    runId: number,
+    head: string,
+    failFastJobNames: string[]
+): Promise<void> {
+  const listJobs = createJobsForWorkflowRunQuery(
+      octokit,
       owner,
       repo,
-      // eslint-disable-next-line @typescript-eslint/camelcase
-      run_id: Number.parseInt(selfRunId)
-    })
-
-    resolvedId = reply.data.workflow_url.split('/').pop() || ''
-    if (!(resolvedId.length > 0)) {
-      throw new Error('Could not resolve workflow')
+      runId,
+  )
+  core.info(`Cancelling runId ${runId} in case one of the ${failFastJobNames} failed`)
+  for await (const item of octokit.paginate.iterator(listJobs)) {
+    for (const job of item.data.jobs) {
+      core.info(`The job name: ${job.name}, Conclusion: ${job.conclusion}`)
+      if (job.conclusion == 'failure' &&
+          failFastJobNames.some(jobNameRegexp => job.name.match(jobNameRegexp) )) {
+        core.info(`Job ${job.name} has failed and it matches one of the ${failFastJobNames} regexps`)
+        core.info(`Cancelling the workflow run: ${runId}, head: ${head}`)
+        await cancelRun(octokit, owner, repo, runId)
+        return
+      }
     }
-  } else {
-    resolvedId = workflowId
   }
+}
 
-  core.info(`Workflow ID is: ${resolvedId}`)
+async function getSelfWorkflowId(
+    octokit: github.GitHub,
+    selfRunId: string,
+    owner: string,
+    repo: string) {
+  let workflowId: string
+  const reply = await octokit.actions.getWorkflowRun({
+    owner,
+    repo,
+    // eslint-disable-next-line @typescript-eslint/camelcase
+    run_id: Number.parseInt(selfRunId)
+  })
+  workflowId = reply.data.workflow_url.split('/').pop() || ''
+  if (!(workflowId.length > 0)) {
+    throw new Error('Could not resolve workflow')
+  }
+  return workflowId
+}
 
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  const sorted = new treemap.TreeMap<number, any>()
+async function getSortedWorkflowRuns(
+    octokit: github.GitHub,
+    createListRunQuery: CallableFunction,
+  ): Promise<treemap.TreeMap<number, Octokit.ActionsListWorkflowRunsResponseWorkflowRunsItem>>{
+  const sortedWorkflowRuns = new treemap.TreeMap<number, any>()
   for (const status of ['queued', 'in_progress']) {
-    const listRuns = createRunsQuery(
-      octokit,
-      owner,
-      repo,
-      resolvedId,
-      status,
-      branch,
-      event
-    )
+    const listRuns = await createListRunQuery(status)
     for await (const item of octokit.paginate.iterator(listRuns)) {
       // There is some sort of bug where the pagination URLs point to a
       // different endpoint URL which trips up the resulting representation
       // In that case, fallback to the actual REST 'workflow_runs' property
       const elements =
-        item.data.length === undefined ? item.data.workflow_runs : item.data
+          item.data.length === undefined ? item.data.workflow_runs : item.data
 
       for (const element of elements) {
-        sorted.set(element.run_number, element)
+        sortedWorkflowRuns.set(element.run_number, element)
       }
     }
   }
+  core.info(`Found runs: ${Array.from(sortedWorkflowRuns.backward()).map(t => t[0])}`)
+  return sortedWorkflowRuns
+}
 
-  // If a workflow was provided process everything
-  let matched = workflowId !== undefined
-  const heads = new Set()
-  for (const entry of sorted.backward()) {
-    const element = entry[1]
+function shouldRunBeSkipped(runItem: Octokit.ActionsListWorkflowRunsResponseWorkflowRunsItem) {
+  if ('completed' === runItem.status.toString()) {
+    core.info(`Skip completed run: ${runItem.id}`)
+    return true
+  }
+
+  if (!['push', 'pull_request'].includes(runItem.event.toString())) {
+    core.info(`Skip run: ${runItem.id} as it is neither push nor pull_request (${runItem.event}`)
+    return true
+  }
+  return false
+}
+
+async function cancelRun(
+    octokit: github.GitHub,
+    owner: string,
+    repo: string,
+    id: number,
+): Promise<void> {
+  let reply
+  try {
+    reply = await octokit.actions.cancelWorkflowRun({
+      owner: owner,
+      repo: repo,
+      // eslint-disable-next-line @typescript-eslint/camelcase
+      run_id: id
+    })
+    core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`)
+  } catch (error) {
     core.info(
-      `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
+        `[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`
     )
+  }
+}
+
 
+// Kills past runs for my own workflow.
+async function findAndCancelPastRunsForSelf(
+    octokit: github.GitHub,
+    selfRunId: string,
+    owner: string,
+    repo: string,
+    branch: string,
+    eventName: string,
+): Promise<void> {
+  core.info(`findAndCancelPastRunsForSelf:  ${selfRunId}, ${owner}, ${repo}, ${branch}, ${eventName}`)
+  const workflowId = await getSelfWorkflowId(octokit, selfRunId, owner, repo)
+  core.info(`My own workflow ID is: ${workflowId}`)
+  const sortedWorkflowRuns = await getSortedWorkflowRuns(
+      octokit,function(status: string) {
+        return createListRunsQueryForSelfRun(octokit, owner, repo, workflowId,
+            status, branch, eventName )
+      }
+  )
+  let matched = false
+  const headsToRunIdMap = new Map<string, number>()
+  for (const [key, runItem] of sortedWorkflowRuns.backward()) {
+    core.info(
+      `Run number: ${key}, RunId: ${runItem.id}, URL: ${runItem.workflow_url}. Status ${runItem.status}`
+    )
     if (!matched) {
-      if (element.id.toString() !== selfRunId) {
-        // Skip everything up to this run
+      if (runItem.id.toString() !== selfRunId) {
+        core.info(`Skip run ${runItem.id} as it was started before my own id: ${selfRunId}`)
         continue
       }
-
       matched = true
-      core.info(`Matched ${selfRunId}`)
+      core.info(`Matched ${selfRunId}. Reached my own ID, now looping through all remaining runs/`)
+      core.info("I will cancel all except the first for each 'head' available")
     }
-
-    if (
-      'completed' === element.status.toString() ||
-      !['push', 'pull_request'].includes(element.event.toString())
-    ) {
+    if (shouldRunBeSkipped(runItem)){
       continue
     }
-
-    // This is a set of one in the non-schedule case, otherwise everything is a candidate
-    const head = `${element.head_repository.full_name}/${element.head_branch}`
-    if (!heads.has(head)) {
-      core.info(`First: ${head}`)
-      heads.add(head)
+    // Head of the run
+    const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`
+    if (!headsToRunIdMap.has(head)) {
+      core.info(`First run for the head: ${head}. Skipping it. Next ones with same head will be cancelled.`)
+      headsToRunIdMap.set(head, runItem.id)
       continue
     }
-
-    core.info(`Cancelling: ${head}`)
-
-    await cancelRun(octokit, owner, repo, element.id)
+    core.info(`Cancelling run: ${runItem.id}, head ${head}.`)
+    core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`)
+    await cancelRun(octokit, owner, repo, runItem.id)
   }
 }
 
-async function run(): Promise<void> {
-  try {
-    const token = core.getInput('token')
-
-    core.info(token)
-
-    const selfRunId = getRequiredEnv('GITHUB_RUN_ID')
-    const repository = getRequiredEnv('GITHUB_REPOSITORY')
-    const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
-
-    const [owner, repo] = repository.split('/')
-    const branchPrefix = 'refs/heads/'
-    const tagPrefix = 'refs/tags/'
+// Kills past runs for my own workflow.
+async function findAndCancelPastRunsForSchedule(
+    octokit: github.GitHub,
+    workflowId: string,
+    owner: string,
+    repo: string,
+    failFastJobNames?: string[],
+): Promise<void> {
+  core.info(`findAndCancelPastRunsForSchedule: ${owner}, ${workflowId}, ${repo}`)
 
-    if ('schedule' === eventName) {
-      const workflowId = core.getInput('workflow')
-      if (!(workflowId.length > 0)) {
-        throw new Error('Workflow must be specified for schedule event type')
+  const sortedWorkflowRuns = await getSortedWorkflowRuns(
+      octokit,function(status: string) {
+        return createListRunsQueryForAllRuns(octokit, owner, repo, workflowId, status)
       }
-      await cancelDuplicates(token, selfRunId, owner, repo, workflowId)
-      return
-    }
+  )
 
-    if (!['push', 'pull_request'].includes(eventName)) {
-      core.info('Skipping unsupported event')
-      return
-    }
+  const headsToRunIdMap = new Map<string, number>()
+  for (const [key, runItem] of sortedWorkflowRuns.backward()) {
+    core.info(
+        ` ${key} ${runItem.id} : ${runItem.workflow_url} : ${runItem.status} : ${runItem.run_number}`
+    )
 
-    const pullRequest = 'pull_request' === eventName
+    if (shouldRunBeSkipped(runItem)){
+      continue
+    }
 
-    let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF')
-    if (!pullRequest && !branch.startsWith(branchPrefix)) {
-      if (branch.startsWith(tagPrefix)) {
-        core.info(`Skipping tag build`)
-        return
+    // Head of the run
+    const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`
+    if (!headsToRunIdMap.has(head)) {
+      core.info(`First run for the head: ${head}. Next runs with the same head will be cancelled.`)
+      headsToRunIdMap.set(head, runItem.id)
+      if (failFastJobNames !== undefined) {
+        core.info("Checking if the head run failed in specified jobs")
+        await cancelOnFailFastJobsFailed(octokit, owner, repo, runItem.id, head, failFastJobNames)
+      } else {
+        core.info("Skipping the head run.")
       }
-      const message = `${branch} was not an expected branch ref (refs/heads/).`
-      throw new Error(message)
+      continue
     }
-    branch = branch.replace(branchPrefix, '')
-
-    core.info(
-      `Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`
-    )
-
-    cancelDuplicates(
-      token,
-      selfRunId,
-      owner,
-      repo,
-      undefined,
-      branch,
-      eventName
-    )
-  } catch (error) {
-    core.setFailed(error.message)
+    core.info(`Cancelling run: ${runItem.id}, head ${head}.`)
+    core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`)
+    await cancelRun(octokit, owner, repo, runItem.id)
   }
 }
 
-async function cancelRun(
-  // eslint-disable-next-line @typescript-eslint/no-explicit-any
-  octokit: any,
-  owner: string,
-  repo: string,
-  id: string
-): Promise<void> {
-  let reply
-  try {
-    reply = await octokit.actions.cancelWorkflowRun({
-      owner,
-      repo,
-      // eslint-disable-next-line @typescript-eslint/camelcase
-      run_id: id
-    })
-    core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`)
-  } catch (error) {
-    core.info(
-      `[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`
-    )
-  }
-}
 
 function getRequiredEnv(key: string): string {
   const value = process.env[key]
@@ -221,4 +267,70 @@ function getRequiredEnv(key: string): string {
   return value
 }
 
-run()
+
+async function runScheduledRun(octokit: github.GitHub, owner: string, repo: string) {
+  const workflowId = core.getInput('workflow')
+  if (!(workflowId.length > 0)) {
+    core.setFailed('Workflow must be specified for schedule event type')
+    return
+  }
+  const failFastJobNames =
+      JSON.parse(core.getInput('failFastJobNames'))
+  if (failFastJobNames !== undefined) {
+    core.info(`Checking also if last run failed in one of the jobs: ${failFastJobNames}`)
+  }
+
+  await findAndCancelPastRunsForSchedule(octokit, workflowId, owner, repo, failFastJobNames)
+  return
+}
+
+async function runRegularRun(
+    octokit: github.GitHub,
+    selfRunId: string,
+    owner:string,
+    repo:string,
+    eventName: string) {
+  const pullRequest = 'pull_request' === eventName
+  const branchPrefix = 'refs/heads/'
+  const tagPrefix = 'refs/tags/'
+
+  let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF')
+  if (!pullRequest && !branch.startsWith(branchPrefix)) {
+    if (branch.startsWith(tagPrefix)) {
+      core.info(`Skipping tag build`)
+      return
+    }
+    core.setFailed(`${branch} was not an expected branch ref (refs/heads/).`)
+    return
+  }
+  branch = branch.replace(branchPrefix, '')
+
+  core.info(
+      `Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`
+  )
+
+  await findAndCancelPastRunsForSelf(octokit, selfRunId, owner, repo, branch, eventName)
+
+}
+
+async function run(): Promise<void> {
+  const token = core.getInput('token')
+  const octokit = new github.GitHub(token)
+  core.info(`Starting checking for workflows to cancel`)
+  const selfRunId = getRequiredEnv('GITHUB_RUN_ID')
+  const repository = getRequiredEnv('GITHUB_REPOSITORY')
+  const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
+
+  const [owner, repo] = repository.split('/')
+
+  if ('schedule' === eventName) {
+    await runScheduledRun(octokit, owner, repo);
+  } else if (!['push', 'pull_request'].includes(eventName)) {
+    core.info('Skipping unsupported event')
+    return
+  } else {
+    await runRegularRun(octokit, selfRunId, owner, repo, eventName)
+  }
+}
+
+run().then(() => core.info("Cancel complete")).catch(e => core.setFailed(e.message))


[airflow-cancel-workflow-runs] 27/44: Fixed docs

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 9fd86165ce7966ab9e9277f9603e281b8ae8bbca
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Jul 26 17:01:53 2020 +0200

    Fixed docs
---
 README.md              |  6 +++---
 __tests__/main.test.ts | 27 ---------------------------
 2 files changed, 3 insertions(+), 30 deletions(-)

diff --git a/README.md b/README.md
index 5c77714..16c7a62 100644
--- a/README.md
+++ b/README.md
@@ -60,7 +60,7 @@ cancel-runs:
   if: github.repository == 'foo-org/my-repo'
   runs-on: ubuntu-latest
     steps:
-      - uses: n1hility/cancel-previous-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v1
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           workflow: my-heavy-workflow.yml
@@ -82,7 +82,7 @@ cancel-runs:
   if: github.repository == 'foo-org/my-repo'
   runs-on: ubuntu-latest
     steps:
-      - uses: n1hility/cancel-previous-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v1
         with:
           token: ${{ secrets.GITHUB_TOKEN }}
           workflow: my-heavy-workflow.yml
@@ -97,7 +97,7 @@ cancel-runs:
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v1
-    - uses: n1hility/cancel-previous-runs@v2
+    - uses: potiuk/cancel-workflow-runs@v1
       with:
         token: ${{ secrets.GITHUB_TOKEN }}
 ```
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index ab5dc2b..6dd468f 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -3,30 +3,3 @@ import * as cp from 'child_process'
 import * as path from 'path'
 
 test('no op', () => {})
-
-// shows how the runner will run a javascript action with env / stdout protocol
-// test('test runs', () => {
-//   const ip = path.join(__dirname, '..', 'lib', 'main.js')
-//   process.env['INPUT_TOKEN'] = ''
-//   process.env['INPUT_WORKFLOW'] = 'ci-actions.yml'
-//   process.env['GITHUB_RUN_ID'] = '41374869' //'33782469'
-//   process.env['GITHUB_REPOSITORY'] = ''
-//   //process.env['GITHUB_HEAD_REF'] = 'refs/heads/n1hility-patch-5'
-//   //process.env['GITHUB_REF'] = 'refs/heads/master'
-//   // process.env['GITHUB_EVENT_NAME'] = 'push'
-//   process.env['GITHUB_EVENT_NAME'] = 'schedule'
-
-//   //   process.env['GITHUB_RUN_ID'] = '35599067'
-//   //   process.env['GITHUB_REPOSITORY'] = ''
-//   //   process.env['GITHUB_REF'] = 'refs/heads/master'
-//   //   process.env['GITHUB_EVENT_NAME'] = 'push'
-
-//   const options: cp.ExecSyncOptions = {
-//     env: process.env
-//   }
-//   try {
-//     console.log(cp.execSync(`node ${ip}`, options).toString())
-//   } catch (error) {
-//     console.log('Error stdout =  ' + error.stdout.toString())
-//   }
-// })


[airflow-cancel-workflow-runs] 12/44: Also filteer non pr and push events

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 70e4efc68f49a9d920814ec5182ac049db2d77af
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 18 23:00:27 2020 -0600

    Also filteer non pr and push events
---
 src/main.ts | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/src/main.ts b/src/main.ts
index 2321b22..35ae27f 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -111,7 +111,7 @@ async function cancelDuplicates(
 
     if (
       'completed' === element.status.toString() ||
-      !['push', 'pull_request'].includes(element.event.toString)
+      !['push', 'pull_request'].includes(element.event.toString())
     ) {
       continue
     }


[airflow-cancel-workflow-runs] 11/44: Also filteer non pr and push events

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit beb7c0e54fcf533a777cc94eaadbcc9dffe31e6b
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 18 22:54:26 2020 -0600

    Also filteer non pr and push events
---
 dist/index.js | 3 ++-
 src/main.ts   | 5 ++++-
 2 files changed, 6 insertions(+), 2 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index ac890d9..08cf9c8 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1546,7 +1546,8 @@ function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, eve
                 matched = true;
                 core.info(`Matched ${selfRunId}`);
             }
-            if ('completed' === element.status.toString()) {
+            if ('completed' === element.status.toString() ||
+                !['push', 'pull_request'].includes(element.event.toString)) {
                 continue;
             }
             // This is a set of one in the non-schedule case, otherwise everything is a candidate
diff --git a/src/main.ts b/src/main.ts
index 560f836..2321b22 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -109,7 +109,10 @@ async function cancelDuplicates(
       core.info(`Matched ${selfRunId}`)
     }
 
-    if ('completed' === element.status.toString()) {
+    if (
+      'completed' === element.status.toString() ||
+      !['push', 'pull_request'].includes(element.event.toString)
+    ) {
       continue
     }
 


[airflow-cancel-workflow-runs] 01/44: Initial impl

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 588db533970801c7b3707ff5bf7d3b5c938bfd5e
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Mon Feb 3 21:21:55 2020 -0600

    Initial impl
---
 .eslintignore                 |     3 +
 .eslintrc.json                |    58 +
 .github/workflows/another.yml |    13 +
 .github/workflows/test.yml    |    20 +
 .gitignore                    |    99 +
 .prettierignore               |     3 +
 .prettierrc.json              |    11 +
 LICENSE                       |    22 +
 README.md                     |    28 +
 __tests__/main.test.ts        |    23 +
 action.yml                    |    10 +
 dist/index.js                 | 25297 ++++++++++++++++++++++++++++++++++++++++
 jest.config.js                |    11 +
 package-lock.json             |  6737 +++++++++++
 package.json                  |    47 +
 src/main.ts                   |    96 +
 tsconfig.json                 |    12 +
 17 files changed, 32490 insertions(+)

diff --git a/.eslintignore b/.eslintignore
new file mode 100644
index 0000000..2186947
--- /dev/null
+++ b/.eslintignore
@@ -0,0 +1,3 @@
+dist/
+lib/
+node_modules/
\ No newline at end of file
diff --git a/.eslintrc.json b/.eslintrc.json
new file mode 100644
index 0000000..09d81aa
--- /dev/null
+++ b/.eslintrc.json
@@ -0,0 +1,58 @@
+{
+    "plugins": ["jest", "@typescript-eslint"],
+    "extends": ["plugin:github/es6"],
+    "parser": "@typescript-eslint/parser",
+    "parserOptions": {
+      "ecmaVersion": 9,
+      "sourceType": "module",
+      "project": "./tsconfig.json"
+    },
+    "rules": {
+      "eslint-comments/no-use": "off",
+      "import/no-namespace": "off",
+      "no-unused-vars": "off",
+      "@typescript-eslint/no-unused-vars": "error",
+      "@typescript-eslint/explicit-member-accessibility": ["error", {"accessibility": "no-public"}],
+      "@typescript-eslint/no-require-imports": "error",
+      "@typescript-eslint/array-type": "error",
+      "@typescript-eslint/await-thenable": "error",
+      "@typescript-eslint/ban-ts-ignore": "error",
+      "camelcase": "off",
+      "@typescript-eslint/camelcase": "error",
+      "@typescript-eslint/class-name-casing": "error",
+      "@typescript-eslint/explicit-function-return-type": ["error", {"allowExpressions": true}],
+      "@typescript-eslint/func-call-spacing": ["error", "never"],
+      "@typescript-eslint/generic-type-naming": ["error", "^[A-Z][A-Za-z]*$"],
+      "@typescript-eslint/no-array-constructor": "error",
+      "@typescript-eslint/no-empty-interface": "error",
+      "@typescript-eslint/no-explicit-any": "error",
+      "@typescript-eslint/no-extraneous-class": "error",
+      "@typescript-eslint/no-for-in-array": "error",
+      "@typescript-eslint/no-inferrable-types": "error",
+      "@typescript-eslint/no-misused-new": "error",
+      "@typescript-eslint/no-namespace": "error",
+      "@typescript-eslint/no-non-null-assertion": "warn",
+      "@typescript-eslint/no-object-literal-type-assertion": "error",
+      "@typescript-eslint/no-unnecessary-qualifier": "error",
+      "@typescript-eslint/no-unnecessary-type-assertion": "error",
+      "@typescript-eslint/no-useless-constructor": "error",
+      "@typescript-eslint/no-var-requires": "error",
+      "@typescript-eslint/prefer-for-of": "warn",
+      "@typescript-eslint/prefer-function-type": "warn",
+      "@typescript-eslint/prefer-includes": "error",
+      "@typescript-eslint/prefer-interface": "error",
+      "@typescript-eslint/prefer-string-starts-ends-with": "error",
+      "@typescript-eslint/promise-function-async": "error",
+      "@typescript-eslint/require-array-sort-compare": "error",
+      "@typescript-eslint/restrict-plus-operands": "error",
+      "semi": "off",
+      "@typescript-eslint/semi": ["error", "never"],
+      "@typescript-eslint/type-annotation-spacing": "error",
+      "@typescript-eslint/unbound-method": "error"
+    },
+    "env": {
+      "node": true,
+      "es6": true,
+      "jest/globals": true
+    }
+  }
\ No newline at end of file
diff --git a/.github/workflows/another.yml b/.github/workflows/another.yml
new file mode 100644
index 0000000..e0d2524
--- /dev/null
+++ b/.github/workflows/another.yml
@@ -0,0 +1,13 @@
+name: "build-test"
+on: # rebuild any PRs and main branch changes
+  pull_request:
+  push:
+
+jobs:
+  build: # make sure build/ci work properly
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - run: |
+       echo Hello
+       sleep 60
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
new file mode 100644
index 0000000..afdd881
--- /dev/null
+++ b/.github/workflows/test.yml
@@ -0,0 +1,20 @@
+name: "build-test"
+on: # rebuild any PRs and main branch changes
+  pull_request:
+  push:
+
+jobs:
+  build: # make sure build/ci work properly
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - run: |
+        npm install
+        npm run all
+  test: # make sure the action works on a clean machine without building
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - uses: ./
+      with: 
+        token: ${{ secrets.GITHUB_TOKEN }}
diff --git a/.gitignore b/.gitignore
new file mode 100644
index 0000000..18e337d
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,99 @@
+# Dependency directory
+node_modules
+
+# Rest pulled from https://github.com/github/gitignore/blob/master/Node.gitignore
+# Logs
+logs
+*.log
+npm-debug.log*
+yarn-debug.log*
+yarn-error.log*
+lerna-debug.log*
+
+# Diagnostic reports (https://nodejs.org/api/report.html)
+report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
+
+# Runtime data
+pids
+*.pid
+*.seed
+*.pid.lock
+
+# Directory for instrumented libs generated by jscoverage/JSCover
+lib-cov
+
+# Coverage directory used by tools like istanbul
+coverage
+*.lcov
+
+# nyc test coverage
+.nyc_output
+
+# Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
+.grunt
+
+# Bower dependency directory (https://bower.io/)
+bower_components
+
+# node-waf configuration
+.lock-wscript
+
+# Compiled binary addons (https://nodejs.org/api/addons.html)
+build/Release
+
+# Dependency directories
+jspm_packages/
+
+# TypeScript v1 declaration files
+typings/
+
+# TypeScript cache
+*.tsbuildinfo
+
+# Optional npm cache directory
+.npm
+
+# Optional eslint cache
+.eslintcache
+
+# Optional REPL history
+.node_repl_history
+
+# Output of 'npm pack'
+*.tgz
+
+# Yarn Integrity file
+.yarn-integrity
+
+# dotenv environment variables file
+.env
+.env.test
+
+# parcel-bundler cache (https://parceljs.org/)
+.cache
+
+# next.js build output
+.next
+
+# nuxt.js build output
+.nuxt
+
+# vuepress build output
+.vuepress/dist
+
+# Serverless directories
+.serverless/
+
+# FuseBox cache
+.fusebox/
+
+# DynamoDB Local files
+.dynamodb/
+
+# OS metadata
+.DS_Store
+Thumbs.db
+
+# Ignore built ts files
+__tests__/runner/*
+lib/**/*
\ No newline at end of file
diff --git a/.prettierignore b/.prettierignore
new file mode 100644
index 0000000..2186947
--- /dev/null
+++ b/.prettierignore
@@ -0,0 +1,3 @@
+dist/
+lib/
+node_modules/
\ No newline at end of file
diff --git a/.prettierrc.json b/.prettierrc.json
new file mode 100644
index 0000000..bbf4311
--- /dev/null
+++ b/.prettierrc.json
@@ -0,0 +1,11 @@
+{
+    "printWidth": 80,
+    "tabWidth": 2,
+    "useTabs": false,
+    "semi": false,
+    "singleQuote": true,
+    "trailingComma": "none",
+    "bracketSpacing": false,
+    "arrowParens": "avoid",
+    "parser": "typescript"
+  }
\ No newline at end of file
diff --git a/LICENSE b/LICENSE
new file mode 100644
index 0000000..a426ef2
--- /dev/null
+++ b/LICENSE
@@ -0,0 +1,22 @@
+
+The MIT License (MIT)
+
+Copyright (c) 2018 GitHub, Inc. and contributors
+
+Permission is hereby granted, free of charge, to any person obtaining a copy
+of this software and associated documentation files (the "Software"), to deal
+in the Software without restriction, including without limitation the rights
+to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
+copies of the Software, and to permit persons to whom the Software is
+furnished to do so, subject to the following conditions:
+
+The above copyright notice and this permission notice shall be included in
+all copies or substantial portions of the Software.
+
+THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
+IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
+FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
+AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
+LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
+OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
+THE SOFTWARE.
\ No newline at end of file
diff --git a/README.md b/README.md
new file mode 100644
index 0000000..22f77f6
--- /dev/null
+++ b/README.md
@@ -0,0 +1,28 @@
+# cancel-previous-runs 
+
+This action cancels all previous runs on the same branch, effectively limiting the resource consumption of the workflow using this action to one run per branch. 
+
+<p><a href="https://github.com/actions/typescript-action/actions"><img alt="typescript-action status" src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
+
+## Usage
+
+This action should be placed as an early step in your workflow (e.g. after chekout), so that it can abort the other running jobs before consuming additional capacity. Additionally, it requires that the running Github Action token (located in the secrets context) be passed as an input parameter so that it can list and cancel workflow runs associated with the workflow's repository.
+
+### Inputs
+
+token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible in the repository, **DO NOT HARDODE A TOKEN ONLY USE A REFERENCE**. 
+
+### Example
+
+```yaml
+  test: 
+    runs-on: ubuntu-latest
+    steps:
+    - uses: actions/checkout@v1
+    - uses: n1hility/cancel-previous-runs@v1
+      with: 
+        token: ${{ secrets.GITHUB_TOKEN }}
+```
+
+## License
+The scripts and documentation in this project are released under the [MIT License](LICENSE)
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
new file mode 100644
index 0000000..bb16f9d
--- /dev/null
+++ b/__tests__/main.test.ts
@@ -0,0 +1,23 @@
+import * as process from 'process'
+import * as cp from 'child_process'
+import * as path from 'path'
+
+test('no op', () => {})
+
+// shows how the runner will run a javascript action with env / stdout protocol
+// test('test runs', () => {
+//   const ip = path.join(__dirname, '..', 'lib', 'main.js')
+//   process.env['INPUT_TOKEN'] = '';
+//   process.env['RUN_ID'] = '33782469';
+//   process.env['GITHUB_REPOSITORY'] = 'protean-project/quarkus-ci';
+//   process.env['GITHUB_REF'] = 'master';
+//   const options: cp.ExecSyncOptions = {
+//     env: process.env
+//   }
+//   try {
+//     console.log(cp.execSync(`node ${ip}`, options).toString())
+//   } catch (error) {
+//     console.log("Error stdout =  " + error.stdout.toString());
+//   }
+
+// })
diff --git a/action.yml b/action.yml
new file mode 100644
index 0000000..f9f98bb
--- /dev/null
+++ b/action.yml
@@ -0,0 +1,10 @@
+name: 'Cancel Previous Runs'
+description: 'Cancels all previous runs of this workflow'
+author: 'n1hility'
+inputs:
+  token:
+    description: The GITHUB_TOKEN secret of this github workflow
+    required: true
+runs:
+  using: 'node12'
+  main: 'dist/index.js'
diff --git a/dist/index.js b/dist/index.js
new file mode 100644
index 0000000..397e2b1
--- /dev/null
+++ b/dist/index.js
@@ -0,0 +1,25297 @@
+module.exports =
+/******/ (function(modules, runtime) { // webpackBootstrap
+/******/ 	"use strict";
+/******/ 	// The module cache
+/******/ 	var installedModules = {};
+/******/
+/******/ 	// The require function
+/******/ 	function __webpack_require__(moduleId) {
+/******/
+/******/ 		// Check if module is in cache
+/******/ 		if(installedModules[moduleId]) {
+/******/ 			return installedModules[moduleId].exports;
+/******/ 		}
+/******/ 		// Create a new module (and put it into the cache)
+/******/ 		var module = installedModules[moduleId] = {
+/******/ 			i: moduleId,
+/******/ 			l: false,
+/******/ 			exports: {}
+/******/ 		};
+/******/
+/******/ 		// Execute the module function
+/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
+/******/
+/******/ 		// Flag the module as loaded
+/******/ 		module.l = true;
+/******/
+/******/ 		// Return the exports of the module
+/******/ 		return module.exports;
+/******/ 	}
+/******/
+/******/
+/******/ 	__webpack_require__.ab = __dirname + "/";
+/******/
+/******/ 	// the startup function
+/******/ 	function startup() {
+/******/ 		// Load entry module and return exports
+/******/ 		return __webpack_require__(131);
+/******/ 	};
+/******/
+/******/ 	// run startup
+/******/ 	return startup();
+/******/ })
+/************************************************************************/
+/******/ ({
+
+/***/ 0:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+const { requestLog } = __webpack_require__(916);
+const {
+  restEndpointMethods
+} = __webpack_require__(842);
+
+const Core = __webpack_require__(529);
+
+const CORE_PLUGINS = [
+  __webpack_require__(190),
+  __webpack_require__(19), // deprecated: remove in v17
+  requestLog,
+  __webpack_require__(148),
+  restEndpointMethods,
+  __webpack_require__(430),
+
+  __webpack_require__(850) // deprecated: remove in v17
+];
+
+const OctokitRest = Core.plugin(CORE_PLUGINS);
+
+function DeprecatedOctokit(options) {
+  const warn =
+    options && options.log && options.log.warn
+      ? options.log.warn
+      : console.warn;
+  warn(
+    '[@octokit/rest] `const Octokit = require("@octokit/rest")` is deprecated. Use `const { Octokit } = require("@octokit/rest")` instead'
+  );
+  return new OctokitRest(options);
+}
+
+const Octokit = Object.assign(DeprecatedOctokit, {
+  Octokit: OctokitRest
+});
+
+Object.keys(OctokitRest).forEach(key => {
+  if (OctokitRest.hasOwnProperty(key)) {
+    Octokit[key] = OctokitRest[key];
+  }
+});
+
+module.exports = Octokit;
+
+
+/***/ }),
+
+/***/ 2:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+const os = __webpack_require__(87);
+const macosRelease = __webpack_require__(118);
+const winRelease = __webpack_require__(49);
+
+const osName = (platform, release) => {
+	if (!platform && release) {
+		throw new Error('You can\'t specify a `release` without specifying `platform`');
+	}
+
+	platform = platform || os.platform();
+
+	let id;
+
+	if (platform === 'darwin') {
+		if (!release && os.platform() === 'darwin') {
+			release = os.release();
+		}
+
+		const prefix = release ? (Number(release.split('.')[0]) > 15 ? 'macOS' : 'OS X') : 'macOS';
+		id = release ? macosRelease(release).name : '';
+		return prefix + (id ? ' ' + id : '');
+	}
+
+	if (platform === 'linux') {
+		if (!release && os.platform() === 'linux') {
+			release = os.release();
+		}
+
+		id = release ? release.replace(/^(\d+\.\d+).*/, '$1') : '';
+		return 'Linux' + (id ? ' ' + id : '');
+	}
+
+	if (platform === 'win32') {
+		if (!release && os.platform() === 'win32') {
+			release = os.release();
+		}
+
+		id = release ? winRelease(release) : '';
+		return 'Windows' + (id ? ' ' + id : '');
+	}
+
+	return platform;
+};
+
+module.exports = osName;
+
+
+/***/ }),
+
+/***/ 9:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+var once = __webpack_require__(969);
+
+var noop = function() {};
+
+var isRequest = function(stream) {
+	return stream.setHeader && typeof stream.abort === 'function';
+};
+
+var isChildProcess = function(stream) {
+	return stream.stdio && Array.isArray(stream.stdio) && stream.stdio.length === 3
+};
+
+var eos = function(stream, opts, callback) {
+	if (typeof opts === 'function') return eos(stream, null, opts);
+	if (!opts) opts = {};
+
+	callback = once(callback || noop);
+
+	var ws = stream._writableState;
+	var rs = stream._readableState;
+	var readable = opts.readable || (opts.readable !== false && stream.readable);
+	var writable = opts.writable || (opts.writable !== false && stream.writable);
+	var cancelled = false;
+
+	var onlegacyfinish = function() {
+		if (!stream.writable) onfinish();
+	};
+
+	var onfinish = function() {
+		writable = false;
+		if (!readable) callback.call(stream);
+	};
+
+	var onend = function() {
+		readable = false;
+		if (!writable) callback.call(stream);
+	};
+
+	var onexit = function(exitCode) {
+		callback.call(stream, exitCode ? new Error('exited with error code: ' + exitCode) : null);
+	};
+
+	var onerror = function(err) {
+		callback.call(stream, err);
+	};
+
+	var onclose = function() {
+		process.nextTick(onclosenexttick);
+	};
+
+	var onclosenexttick = function() {
+		if (cancelled) return;
+		if (readable && !(rs && (rs.ended && !rs.destroyed))) return callback.call(stream, new Error('premature close'));
+		if (writable && !(ws && (ws.ended && !ws.destroyed))) return callback.call(stream, new Error('premature close'));
+	};
+
+	var onrequest = function() {
+		stream.req.on('finish', onfinish);
+	};
+
+	if (isRequest(stream)) {
+		stream.on('complete', onfinish);
+		stream.on('abort', onclose);
+		if (stream.req) onrequest();
+		else stream.on('request', onrequest);
+	} else if (writable && !ws) { // legacy streams
+		stream.on('end', onlegacyfinish);
+		stream.on('close', onlegacyfinish);
+	}
+
+	if (isChildProcess(stream)) stream.on('exit', onexit);
+
+	stream.on('end', onend);
+	stream.on('finish', onfinish);
+	if (opts.error !== false) stream.on('error', onerror);
+	stream.on('close', onclose);
+
+	return function() {
+		cancelled = true;
+		stream.removeListener('complete', onfinish);
+		stream.removeListener('abort', onclose);
+		stream.removeListener('request', onrequest);
+		if (stream.req) stream.req.removeListener('finish', onfinish);
+		stream.removeListener('end', onlegacyfinish);
+		stream.removeListener('close', onlegacyfinish);
+		stream.removeListener('finish', onfinish);
+		stream.removeListener('exit', onexit);
+		stream.removeListener('end', onend);
+		stream.removeListener('error', onerror);
+		stream.removeListener('close', onclose);
+	};
+};
+
+module.exports = eos;
+
+
+/***/ }),
+
+/***/ 11:
+/***/ (function(module) {
+
+// Returns a wrapper function that returns a wrapped callback
+// The wrapper function should do some stuff, and return a
+// presumably different callback function.
+// This makes sure that own properties are retained, so that
+// decorations and such are not lost along the way.
+module.exports = wrappy
+function wrappy (fn, cb) {
+  if (fn && cb) return wrappy(fn)(cb)
+
+  if (typeof fn !== 'function')
+    throw new TypeError('need wrapper function')
+
+  Object.keys(fn).forEach(function (k) {
+    wrapper[k] = fn[k]
+  })
+
+  return wrapper
+
+  function wrapper() {
+    var args = new Array(arguments.length)
+    for (var i = 0; i < args.length; i++) {
+      args[i] = arguments[i]
+    }
+    var ret = fn.apply(this, args)
+    var cb = args[args.length-1]
+    if (typeof ret === 'function' && ret !== cb) {
+      Object.keys(cb).forEach(function (k) {
+        ret[k] = cb[k]
+      })
+    }
+    return ret
+  }
+}
+
+
+/***/ }),
+
+/***/ 16:
+/***/ (function(module) {
+
+module.exports = require("tls");
+
+/***/ }),
+
+/***/ 18:
+/***/ (function() {
+
+eval("require")("encoding");
+
+
+/***/ }),
+
+/***/ 19:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticationPlugin;
+
+const { Deprecation } = __webpack_require__(692);
+const once = __webpack_require__(969);
+
+const deprecateAuthenticate = once((log, deprecation) => log.warn(deprecation));
+
+const authenticate = __webpack_require__(674);
+const beforeRequest = __webpack_require__(471);
+const requestError = __webpack_require__(349);
+
+function authenticationPlugin(octokit, options) {
+  if (options.auth) {
+    octokit.authenticate = () => {
+      deprecateAuthenticate(
+        octokit.log,
+        new Deprecation(
+          '[@octokit/rest] octokit.authenticate() is deprecated and has no effect when "auth" option is set on Octokit constructor'
+        )
+      );
+    };
+    return;
+  }
+  const state = {
+    octokit,
+    auth: false
+  };
+  octokit.authenticate = authenticate.bind(null, state);
+  octokit.hook.before("request", beforeRequest.bind(null, state));
+  octokit.hook.error("request", requestError.bind(null, state));
+}
+
+
+/***/ }),
+
+/***/ 20:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+
+const cp = __webpack_require__(129);
+const parse = __webpack_require__(568);
+const enoent = __webpack_require__(881);
+
+function spawn(command, args, options) {
+    // Parse the arguments
+    const parsed = parse(command, args, options);
+
+    // Spawn the child process
+    const spawned = cp.spawn(parsed.command, parsed.args, parsed.options);
+
+    // Hook into child process "exit" event to emit an error if the command
+    // does not exists, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
+    enoent.hookChildProcess(spawned, parsed);
+
+    return spawned;
+}
+
+function spawnSync(command, args, options) {
+    // Parse the arguments
+    const parsed = parse(command, args, options);
+
+    // Spawn the child process
+    const result = cp.spawnSync(parsed.command, parsed.args, parsed.options);
+
+    // Analyze if the command does not exist, see: https://github.com/IndigoUnited/node-cross-spawn/issues/16
+    result.error = result.error || enoent.verifyENOENTSync(result.status, parsed);
+
+    return result;
+}
+
+module.exports = spawn;
+module.exports.spawn = spawn;
+module.exports.sync = spawnSync;
+
+module.exports._parse = parse;
+module.exports._enoent = enoent;
+
+
+/***/ }),
+
+/***/ 39:
+/***/ (function(module) {
+
+"use strict";
+
+module.exports = opts => {
+	opts = opts || {};
+
+	const env = opts.env || process.env;
+	const platform = opts.platform || process.platform;
+
+	if (platform !== 'win32') {
+		return 'PATH';
+	}
+
+	return Object.keys(env).find(x => x.toUpperCase() === 'PATH') || 'Path';
+};
+
+
+/***/ }),
+
+/***/ 47:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = factory;
+
+const Octokit = __webpack_require__(402);
+const registerPlugin = __webpack_require__(855);
+
+function factory(plugins) {
+  const Api = Octokit.bind(null, plugins || []);
+  Api.plugin = registerPlugin.bind(null, plugins || []);
+  return Api;
+}
+
+
+/***/ }),
+
+/***/ 49:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+const os = __webpack_require__(87);
+const execa = __webpack_require__(955);
+
+// Reference: https://www.gaijin.at/en/lstwinver.php
+const names = new Map([
+	['10.0', '10'],
+	['6.3', '8.1'],
+	['6.2', '8'],
+	['6.1', '7'],
+	['6.0', 'Vista'],
+	['5.2', 'Server 2003'],
+	['5.1', 'XP'],
+	['5.0', '2000'],
+	['4.9', 'ME'],
+	['4.1', '98'],
+	['4.0', '95']
+]);
+
+const windowsRelease = release => {
+	const version = /\d+\.\d/.exec(release || os.release());
+
+	if (release && !version) {
+		throw new Error('`release` argument doesn\'t match `n.n`');
+	}
+
+	const ver = (version || [])[0];
+
+	// Server 2008, 2012 and 2016 versions are ambiguous with desktop versions and must be detected at runtime.
+	// If `release` is omitted or we're on a Windows system, and the version number is an ambiguous version
+	// then use `wmic` to get the OS caption: https://msdn.microsoft.com/en-us/library/aa394531(v=vs.85).aspx
+	// If the resulting caption contains the year 2008, 2012 or 2016, it is a server version, so return a server OS name.
+	if ((!release || release === os.release()) && ['6.1', '6.2', '6.3', '10.0'].includes(ver)) {
+		const stdout = execa.sync('wmic', ['os', 'get', 'Caption']).stdout || '';
+		const year = (stdout.match(/2008|2012|2016/) || [])[0];
+		if (year) {
+			return `Server ${year}`;
+		}
+	}
+
+	return names.get(ver);
+};
+
+module.exports = windowsRelease;
+
+
+/***/ }),
+
+/***/ 87:
+/***/ (function(module) {
+
+module.exports = require("os");
+
+/***/ }),
+
+/***/ 118:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+const os = __webpack_require__(87);
+
+const nameMap = new Map([
+	[19, 'Catalina'],
+	[18, 'Mojave'],
+	[17, 'High Sierra'],
+	[16, 'Sierra'],
+	[15, 'El Capitan'],
+	[14, 'Yosemite'],
+	[13, 'Mavericks'],
+	[12, 'Mountain Lion'],
+	[11, 'Lion'],
+	[10, 'Snow Leopard'],
+	[9, 'Leopard'],
+	[8, 'Tiger'],
+	[7, 'Panther'],
+	[6, 'Jaguar'],
+	[5, 'Puma']
+]);
+
+const macosRelease = release => {
+	release = Number((release || os.release()).split('.')[0]);
+	return {
+		name: nameMap.get(release),
+		version: '10.' + (release - 4)
+	};
+};
+
+module.exports = macosRelease;
+// TODO: remove this in the next major version
+module.exports.default = macosRelease;
+
+
+/***/ }),
+
+/***/ 126:
+/***/ (function(module) {
+
+/**
+ * lodash (Custom Build) <https://lodash.com/>
+ * Build: `lodash modularize exports="npm" -o ./`
+ * Copyright jQuery Foundation and other contributors <https://jquery.org/>
+ * Released under MIT license <https://lodash.com/license>
+ * Based on Underscore.js 1.8.3 <http://underscorejs.org/LICENSE>
+ * Copyright Jeremy Ashkenas, DocumentCloud and Investigative Reporters & Editors
+ */
+
+/** Used as the size to enable large array optimizations. */
+var LARGE_ARRAY_SIZE = 200;
+
+/** Used to stand-in for `undefined` hash values. */
+var HASH_UNDEFINED = '__lodash_hash_undefined__';
+
+/** Used as references for various `Number` constants. */
+var INFINITY = 1 / 0;
+
+/** `Object#toString` result references. */
+var funcTag = '[object Function]',
+    genTag = '[object GeneratorFunction]';
+
+/**
+ * Used to match `RegExp`
+ * [syntax characters](http://ecma-international.org/ecma-262/7.0/#sec-patterns).
+ */
+var reRegExpChar = /[\\^$.*+?()[\]{}|]/g;
+
+/** Used to detect host constructors (Safari). */
+var reIsHostCtor = /^\[object .+?Constructor\]$/;
+
+/** Detect free variable `global` from Node.js. */
+var freeGlobal = typeof global == 'object' && global && global.Object === Object && global;
+
+/** Detect free variable `self`. */
+var freeSelf = typeof self == 'object' && self && self.Object === Object && self;
+
+/** Used as a reference to the global object. */
+var root = freeGlobal || freeSelf || Function('return this')();
+
+/**
+ * A specialized version of `_.includes` for arrays without support for
+ * specifying an index to search from.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludes(array, value) {
+  var length = array ? array.length : 0;
+  return !!length && baseIndexOf(array, value, 0) > -1;
+}
+
+/**
+ * This function is like `arrayIncludes` except that it accepts a comparator.
+ *
+ * @private
+ * @param {Array} [array] The array to inspect.
+ * @param {*} target The value to search for.
+ * @param {Function} comparator The comparator invoked per element.
+ * @returns {boolean} Returns `true` if `target` is found, else `false`.
+ */
+function arrayIncludesWith(array, value, comparator) {
+  var index = -1,
+      length = array ? array.length : 0;
+
+  while (++index < length) {
+    if (comparator(value, array[index])) {
+      return true;
+    }
+  }
+  return false;
+}
+
+/**
+ * The base implementation of `_.findIndex` and `_.findLastIndex` without
+ * support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} predicate The function invoked per iteration.
+ * @param {number} fromIndex The index to search from.
+ * @param {boolean} [fromRight] Specify iterating from right to left.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseFindIndex(array, predicate, fromIndex, fromRight) {
+  var length = array.length,
+      index = fromIndex + (fromRight ? 1 : -1);
+
+  while ((fromRight ? index-- : ++index < length)) {
+    if (predicate(array[index], index, array)) {
+      return index;
+    }
+  }
+  return -1;
+}
+
+/**
+ * The base implementation of `_.indexOf` without `fromIndex` bounds checks.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} value The value to search for.
+ * @param {number} fromIndex The index to search from.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function baseIndexOf(array, value, fromIndex) {
+  if (value !== value) {
+    return baseFindIndex(array, baseIsNaN, fromIndex);
+  }
+  var index = fromIndex - 1,
+      length = array.length;
+
+  while (++index < length) {
+    if (array[index] === value) {
+      return index;
+    }
+  }
+  return -1;
+}
+
+/**
+ * The base implementation of `_.isNaN` without support for number objects.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is `NaN`, else `false`.
+ */
+function baseIsNaN(value) {
+  return value !== value;
+}
+
+/**
+ * Checks if a cache value for `key` exists.
+ *
+ * @private
+ * @param {Object} cache The cache to query.
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function cacheHas(cache, key) {
+  return cache.has(key);
+}
+
+/**
+ * Gets the value at `key` of `object`.
+ *
+ * @private
+ * @param {Object} [object] The object to query.
+ * @param {string} key The key of the property to get.
+ * @returns {*} Returns the property value.
+ */
+function getValue(object, key) {
+  return object == null ? undefined : object[key];
+}
+
+/**
+ * Checks if `value` is a host object in IE < 9.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a host object, else `false`.
+ */
+function isHostObject(value) {
+  // Many host objects are `Object` objects that can coerce to strings
+  // despite having improperly defined `toString` methods.
+  var result = false;
+  if (value != null && typeof value.toString != 'function') {
+    try {
+      result = !!(value + '');
+    } catch (e) {}
+  }
+  return result;
+}
+
+/**
+ * Converts `set` to an array of its values.
+ *
+ * @private
+ * @param {Object} set The set to convert.
+ * @returns {Array} Returns the values.
+ */
+function setToArray(set) {
+  var index = -1,
+      result = Array(set.size);
+
+  set.forEach(function(value) {
+    result[++index] = value;
+  });
+  return result;
+}
+
+/** Used for built-in method references. */
+var arrayProto = Array.prototype,
+    funcProto = Function.prototype,
+    objectProto = Object.prototype;
+
+/** Used to detect overreaching core-js shims. */
+var coreJsData = root['__core-js_shared__'];
+
+/** Used to detect methods masquerading as native. */
+var maskSrcKey = (function() {
+  var uid = /[^.]+$/.exec(coreJsData && coreJsData.keys && coreJsData.keys.IE_PROTO || '');
+  return uid ? ('Symbol(src)_1.' + uid) : '';
+}());
+
+/** Used to resolve the decompiled source of functions. */
+var funcToString = funcProto.toString;
+
+/** Used to check objects for own properties. */
+var hasOwnProperty = objectProto.hasOwnProperty;
+
+/**
+ * Used to resolve the
+ * [`toStringTag`](http://ecma-international.org/ecma-262/7.0/#sec-object.prototype.tostring)
+ * of values.
+ */
+var objectToString = objectProto.toString;
+
+/** Used to detect if a method is native. */
+var reIsNative = RegExp('^' +
+  funcToString.call(hasOwnProperty).replace(reRegExpChar, '\\$&')
+  .replace(/hasOwnProperty|(function).*?(?=\\\()| for .+?(?=\\\])/g, '$1.*?') + '$'
+);
+
+/** Built-in value references. */
+var splice = arrayProto.splice;
+
+/* Built-in method references that are verified to be native. */
+var Map = getNative(root, 'Map'),
+    Set = getNative(root, 'Set'),
+    nativeCreate = getNative(Object, 'create');
+
+/**
+ * Creates a hash object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function Hash(entries) {
+  var index = -1,
+      length = entries ? entries.length : 0;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+/**
+ * Removes all key-value entries from the hash.
+ *
+ * @private
+ * @name clear
+ * @memberOf Hash
+ */
+function hashClear() {
+  this.__data__ = nativeCreate ? nativeCreate(null) : {};
+}
+
+/**
+ * Removes `key` and its value from the hash.
+ *
+ * @private
+ * @name delete
+ * @memberOf Hash
+ * @param {Object} hash The hash to modify.
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function hashDelete(key) {
+  return this.has(key) && delete this.__data__[key];
+}
+
+/**
+ * Gets the hash value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf Hash
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function hashGet(key) {
+  var data = this.__data__;
+  if (nativeCreate) {
+    var result = data[key];
+    return result === HASH_UNDEFINED ? undefined : result;
+  }
+  return hasOwnProperty.call(data, key) ? data[key] : undefined;
+}
+
+/**
+ * Checks if a hash value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf Hash
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function hashHas(key) {
+  var data = this.__data__;
+  return nativeCreate ? data[key] !== undefined : hasOwnProperty.call(data, key);
+}
+
+/**
+ * Sets the hash `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf Hash
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the hash instance.
+ */
+function hashSet(key, value) {
+  var data = this.__data__;
+  data[key] = (nativeCreate && value === undefined) ? HASH_UNDEFINED : value;
+  return this;
+}
+
+// Add methods to `Hash`.
+Hash.prototype.clear = hashClear;
+Hash.prototype['delete'] = hashDelete;
+Hash.prototype.get = hashGet;
+Hash.prototype.has = hashHas;
+Hash.prototype.set = hashSet;
+
+/**
+ * Creates an list cache object.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function ListCache(entries) {
+  var index = -1,
+      length = entries ? entries.length : 0;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+/**
+ * Removes all key-value entries from the list cache.
+ *
+ * @private
+ * @name clear
+ * @memberOf ListCache
+ */
+function listCacheClear() {
+  this.__data__ = [];
+}
+
+/**
+ * Removes `key` and its value from the list cache.
+ *
+ * @private
+ * @name delete
+ * @memberOf ListCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function listCacheDelete(key) {
+  var data = this.__data__,
+      index = assocIndexOf(data, key);
+
+  if (index < 0) {
+    return false;
+  }
+  var lastIndex = data.length - 1;
+  if (index == lastIndex) {
+    data.pop();
+  } else {
+    splice.call(data, index, 1);
+  }
+  return true;
+}
+
+/**
+ * Gets the list cache value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf ListCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function listCacheGet(key) {
+  var data = this.__data__,
+      index = assocIndexOf(data, key);
+
+  return index < 0 ? undefined : data[index][1];
+}
+
+/**
+ * Checks if a list cache value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf ListCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function listCacheHas(key) {
+  return assocIndexOf(this.__data__, key) > -1;
+}
+
+/**
+ * Sets the list cache `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf ListCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the list cache instance.
+ */
+function listCacheSet(key, value) {
+  var data = this.__data__,
+      index = assocIndexOf(data, key);
+
+  if (index < 0) {
+    data.push([key, value]);
+  } else {
+    data[index][1] = value;
+  }
+  return this;
+}
+
+// Add methods to `ListCache`.
+ListCache.prototype.clear = listCacheClear;
+ListCache.prototype['delete'] = listCacheDelete;
+ListCache.prototype.get = listCacheGet;
+ListCache.prototype.has = listCacheHas;
+ListCache.prototype.set = listCacheSet;
+
+/**
+ * Creates a map cache object to store key-value pairs.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [entries] The key-value pairs to cache.
+ */
+function MapCache(entries) {
+  var index = -1,
+      length = entries ? entries.length : 0;
+
+  this.clear();
+  while (++index < length) {
+    var entry = entries[index];
+    this.set(entry[0], entry[1]);
+  }
+}
+
+/**
+ * Removes all key-value entries from the map.
+ *
+ * @private
+ * @name clear
+ * @memberOf MapCache
+ */
+function mapCacheClear() {
+  this.__data__ = {
+    'hash': new Hash,
+    'map': new (Map || ListCache),
+    'string': new Hash
+  };
+}
+
+/**
+ * Removes `key` and its value from the map.
+ *
+ * @private
+ * @name delete
+ * @memberOf MapCache
+ * @param {string} key The key of the value to remove.
+ * @returns {boolean} Returns `true` if the entry was removed, else `false`.
+ */
+function mapCacheDelete(key) {
+  return getMapData(this, key)['delete'](key);
+}
+
+/**
+ * Gets the map value for `key`.
+ *
+ * @private
+ * @name get
+ * @memberOf MapCache
+ * @param {string} key The key of the value to get.
+ * @returns {*} Returns the entry value.
+ */
+function mapCacheGet(key) {
+  return getMapData(this, key).get(key);
+}
+
+/**
+ * Checks if a map value for `key` exists.
+ *
+ * @private
+ * @name has
+ * @memberOf MapCache
+ * @param {string} key The key of the entry to check.
+ * @returns {boolean} Returns `true` if an entry for `key` exists, else `false`.
+ */
+function mapCacheHas(key) {
+  return getMapData(this, key).has(key);
+}
+
+/**
+ * Sets the map `key` to `value`.
+ *
+ * @private
+ * @name set
+ * @memberOf MapCache
+ * @param {string} key The key of the value to set.
+ * @param {*} value The value to set.
+ * @returns {Object} Returns the map cache instance.
+ */
+function mapCacheSet(key, value) {
+  getMapData(this, key).set(key, value);
+  return this;
+}
+
+// Add methods to `MapCache`.
+MapCache.prototype.clear = mapCacheClear;
+MapCache.prototype['delete'] = mapCacheDelete;
+MapCache.prototype.get = mapCacheGet;
+MapCache.prototype.has = mapCacheHas;
+MapCache.prototype.set = mapCacheSet;
+
+/**
+ *
+ * Creates an array cache object to store unique values.
+ *
+ * @private
+ * @constructor
+ * @param {Array} [values] The values to cache.
+ */
+function SetCache(values) {
+  var index = -1,
+      length = values ? values.length : 0;
+
+  this.__data__ = new MapCache;
+  while (++index < length) {
+    this.add(values[index]);
+  }
+}
+
+/**
+ * Adds `value` to the array cache.
+ *
+ * @private
+ * @name add
+ * @memberOf SetCache
+ * @alias push
+ * @param {*} value The value to cache.
+ * @returns {Object} Returns the cache instance.
+ */
+function setCacheAdd(value) {
+  this.__data__.set(value, HASH_UNDEFINED);
+  return this;
+}
+
+/**
+ * Checks if `value` is in the array cache.
+ *
+ * @private
+ * @name has
+ * @memberOf SetCache
+ * @param {*} value The value to search for.
+ * @returns {number} Returns `true` if `value` is found, else `false`.
+ */
+function setCacheHas(value) {
+  return this.__data__.has(value);
+}
+
+// Add methods to `SetCache`.
+SetCache.prototype.add = SetCache.prototype.push = setCacheAdd;
+SetCache.prototype.has = setCacheHas;
+
+/**
+ * Gets the index at which the `key` is found in `array` of key-value pairs.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {*} key The key to search for.
+ * @returns {number} Returns the index of the matched value, else `-1`.
+ */
+function assocIndexOf(array, key) {
+  var length = array.length;
+  while (length--) {
+    if (eq(array[length][0], key)) {
+      return length;
+    }
+  }
+  return -1;
+}
+
+/**
+ * The base implementation of `_.isNative` without bad shim checks.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a native function,
+ *  else `false`.
+ */
+function baseIsNative(value) {
+  if (!isObject(value) || isMasked(value)) {
+    return false;
+  }
+  var pattern = (isFunction(value) || isHostObject(value)) ? reIsNative : reIsHostCtor;
+  return pattern.test(toSource(value));
+}
+
+/**
+ * The base implementation of `_.uniqBy` without support for iteratee shorthands.
+ *
+ * @private
+ * @param {Array} array The array to inspect.
+ * @param {Function} [iteratee] The iteratee invoked per element.
+ * @param {Function} [comparator] The comparator invoked per element.
+ * @returns {Array} Returns the new duplicate free array.
+ */
+function baseUniq(array, iteratee, comparator) {
+  var index = -1,
+      includes = arrayIncludes,
+      length = array.length,
+      isCommon = true,
+      result = [],
+      seen = result;
+
+  if (comparator) {
+    isCommon = false;
+    includes = arrayIncludesWith;
+  }
+  else if (length >= LARGE_ARRAY_SIZE) {
+    var set = iteratee ? null : createSet(array);
+    if (set) {
+      return setToArray(set);
+    }
+    isCommon = false;
+    includes = cacheHas;
+    seen = new SetCache;
+  }
+  else {
+    seen = iteratee ? [] : result;
+  }
+  outer:
+  while (++index < length) {
+    var value = array[index],
+        computed = iteratee ? iteratee(value) : value;
+
+    value = (comparator || value !== 0) ? value : 0;
+    if (isCommon && computed === computed) {
+      var seenIndex = seen.length;
+      while (seenIndex--) {
+        if (seen[seenIndex] === computed) {
+          continue outer;
+        }
+      }
+      if (iteratee) {
+        seen.push(computed);
+      }
+      result.push(value);
+    }
+    else if (!includes(seen, computed, comparator)) {
+      if (seen !== result) {
+        seen.push(computed);
+      }
+      result.push(value);
+    }
+  }
+  return result;
+}
+
+/**
+ * Creates a set object of `values`.
+ *
+ * @private
+ * @param {Array} values The values to add to the set.
+ * @returns {Object} Returns the new set.
+ */
+var createSet = !(Set && (1 / setToArray(new Set([,-0]))[1]) == INFINITY) ? noop : function(values) {
+  return new Set(values);
+};
+
+/**
+ * Gets the data for `map`.
+ *
+ * @private
+ * @param {Object} map The map to query.
+ * @param {string} key The reference key.
+ * @returns {*} Returns the map data.
+ */
+function getMapData(map, key) {
+  var data = map.__data__;
+  return isKeyable(key)
+    ? data[typeof key == 'string' ? 'string' : 'hash']
+    : data.map;
+}
+
+/**
+ * Gets the native function at `key` of `object`.
+ *
+ * @private
+ * @param {Object} object The object to query.
+ * @param {string} key The key of the method to get.
+ * @returns {*} Returns the function if it's native, else `undefined`.
+ */
+function getNative(object, key) {
+  var value = getValue(object, key);
+  return baseIsNative(value) ? value : undefined;
+}
+
+/**
+ * Checks if `value` is suitable for use as unique object key.
+ *
+ * @private
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is suitable, else `false`.
+ */
+function isKeyable(value) {
+  var type = typeof value;
+  return (type == 'string' || type == 'number' || type == 'symbol' || type == 'boolean')
+    ? (value !== '__proto__')
+    : (value === null);
+}
+
+/**
+ * Checks if `func` has its source masked.
+ *
+ * @private
+ * @param {Function} func The function to check.
+ * @returns {boolean} Returns `true` if `func` is masked, else `false`.
+ */
+function isMasked(func) {
+  return !!maskSrcKey && (maskSrcKey in func);
+}
+
+/**
+ * Converts `func` to its source code.
+ *
+ * @private
+ * @param {Function} func The function to process.
+ * @returns {string} Returns the source code.
+ */
+function toSource(func) {
+  if (func != null) {
+    try {
+      return funcToString.call(func);
+    } catch (e) {}
+    try {
+      return (func + '');
+    } catch (e) {}
+  }
+  return '';
+}
+
+/**
+ * Creates a duplicate-free version of an array, using
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * for equality comparisons, in which only the first occurrence of each
+ * element is kept.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Array
+ * @param {Array} array The array to inspect.
+ * @returns {Array} Returns the new duplicate free array.
+ * @example
+ *
+ * _.uniq([2, 1, 2]);
+ * // => [2, 1]
+ */
+function uniq(array) {
+  return (array && array.length)
+    ? baseUniq(array)
+    : [];
+}
+
+/**
+ * Performs a
+ * [`SameValueZero`](http://ecma-international.org/ecma-262/7.0/#sec-samevaluezero)
+ * comparison between two values to determine if they are equivalent.
+ *
+ * @static
+ * @memberOf _
+ * @since 4.0.0
+ * @category Lang
+ * @param {*} value The value to compare.
+ * @param {*} other The other value to compare.
+ * @returns {boolean} Returns `true` if the values are equivalent, else `false`.
+ * @example
+ *
+ * var object = { 'a': 1 };
+ * var other = { 'a': 1 };
+ *
+ * _.eq(object, object);
+ * // => true
+ *
+ * _.eq(object, other);
+ * // => false
+ *
+ * _.eq('a', 'a');
+ * // => true
+ *
+ * _.eq('a', Object('a'));
+ * // => false
+ *
+ * _.eq(NaN, NaN);
+ * // => true
+ */
+function eq(value, other) {
+  return value === other || (value !== value && other !== other);
+}
+
+/**
+ * Checks if `value` is classified as a `Function` object.
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is a function, else `false`.
+ * @example
+ *
+ * _.isFunction(_);
+ * // => true
+ *
+ * _.isFunction(/abc/);
+ * // => false
+ */
+function isFunction(value) {
+  // The use of `Object#toString` avoids issues with the `typeof` operator
+  // in Safari 8-9 which returns 'object' for typed array and other constructors.
+  var tag = isObject(value) ? objectToString.call(value) : '';
+  return tag == funcTag || tag == genTag;
+}
+
+/**
+ * Checks if `value` is the
+ * [language type](http://www.ecma-international.org/ecma-262/7.0/#sec-ecmascript-language-types)
+ * of `Object`. (e.g. arrays, functions, objects, regexes, `new Number(0)`, and `new String('')`)
+ *
+ * @static
+ * @memberOf _
+ * @since 0.1.0
+ * @category Lang
+ * @param {*} value The value to check.
+ * @returns {boolean} Returns `true` if `value` is an object, else `false`.
+ * @example
+ *
+ * _.isObject({});
+ * // => true
+ *
+ * _.isObject([1, 2, 3]);
+ * // => true
+ *
+ * _.isObject(_.noop);
+ * // => true
+ *
+ * _.isObject(null);
+ * // => false
+ */
+function isObject(value) {
+  var type = typeof value;
+  return !!value && (type == 'object' || type == 'function');
+}
+
+/**
+ * This method returns `undefined`.
+ *
+ * @static
+ * @memberOf _
+ * @since 2.3.0
+ * @category Util
+ * @example
+ *
+ * _.times(2, _.noop);
+ * // => [undefined, undefined]
+ */
+function noop() {
+  // No operation performed.
+}
+
+module.exports = uniq;
+
+
+/***/ }),
+
+/***/ 129:
+/***/ (function(module) {
+
+module.exports = require("child_process");
+
+/***/ }),
+
+/***/ 131:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const github = __importStar(__webpack_require__(469));
+const core = __importStar(__webpack_require__(393));
+function run() {
+    return __awaiter(this, void 0, void 0, function* () {
+        try {
+            const token = core.getInput('token');
+            const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
+            const repository = getRequiredEnv('GITHUB_REPOSITORY');
+            const [owner, repo] = repository.split('/');
+            const refsPrefix = 'refs/heads/';
+            let branch = getRequiredEnv('GITHUB_REF');
+            if (!branch.startsWith(refsPrefix)) {
+                const message = `${branch} was not an expected branch ref (refs/heads/).`;
+                throw new Error(message);
+            }
+            branch = branch.replace(refsPrefix, '');
+            core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
+            const octokit = new github.GitHub(token);
+            const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
+                owner,
+                repo,
+                branch,
+                event: 'push'
+            });
+            // eslint-disable-next-line @typescript-eslint/no-explicit-any
+            yield octokit.paginate(listRuns).then((runs) => __awaiter(this, void 0, void 0, function* () {
+                let matched = false;
+                let workflow = '';
+                for (const element of runs) {
+                    core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
+                    if (!matched) {
+                        if (element.id.toString() === selfRunId) {
+                            matched = true;
+                            workflow = element.workflow_url;
+                        }
+                        // Skip everything up to and matching this run
+                        continue;
+                    }
+                    // Only cancel jobs with the same workflow
+                    if (workflow === element.workflow_url &&
+                        element.status.toString() !== 'completed') {
+                        yield cancelRun(octokit, owner, repo, element.id);
+                    }
+                }
+            }));
+        }
+        catch (error) {
+            core.setFailed(error.message);
+        }
+    });
+}
+function cancelRun(
+// eslint-disable-next-line @typescript-eslint/no-explicit-any
+octokit, owner, repo, id) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let reply;
+        try {
+            reply = yield octokit.actions.cancelWorkflowRun({
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                run_id: id
+            });
+            core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`);
+        }
+        catch (error) {
+            core.info(`[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`);
+        }
+    });
+}
+function getRequiredEnv(key) {
+    const value = process.env[key];
+    if (value === undefined) {
+        const message = `${key} was not defined.`;
+        throw new Error(message);
+    }
+    return value;
+}
+run();
+
+
+/***/ }),
+
+/***/ 141:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+var net = __webpack_require__(631);
+var tls = __webpack_require__(16);
+var http = __webpack_require__(605);
+var https = __webpack_require__(211);
+var events = __webpack_require__(614);
+var assert = __webpack_require__(357);
+var util = __webpack_require__(669);
+
+
+exports.httpOverHttp = httpOverHttp;
+exports.httpsOverHttp = httpsOverHttp;
+exports.httpOverHttps = httpOverHttps;
+exports.httpsOverHttps = httpsOverHttps;
+
+
+function httpOverHttp(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = http.request;
+  return agent;
+}
+
+function httpsOverHttp(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = http.request;
+  agent.createSocket = createSecureSocket;
+  agent.defaultPort = 443;
+  return agent;
+}
+
+function httpOverHttps(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = https.request;
+  return agent;
+}
+
+function httpsOverHttps(options) {
+  var agent = new TunnelingAgent(options);
+  agent.request = https.request;
+  agent.createSocket = createSecureSocket;
+  agent.defaultPort = 443;
+  return agent;
+}
+
+
+function TunnelingAgent(options) {
+  var self = this;
+  self.options = options || {};
+  self.proxyOptions = self.options.proxy || {};
+  self.maxSockets = self.options.maxSockets || http.Agent.defaultMaxSockets;
+  self.requests = [];
+  self.sockets = [];
+
+  self.on('free', function onFree(socket, host, port, localAddress) {
+    var options = toOptions(host, port, localAddress);
+    for (var i = 0, len = self.requests.length; i < len; ++i) {
+      var pending = self.requests[i];
+      if (pending.host === options.host && pending.port === options.port) {
+        // Detect the request to connect same origin server,
+        // reuse the connection.
+        self.requests.splice(i, 1);
+        pending.request.onSocket(socket);
+        return;
+      }
+    }
+    socket.destroy();
+    self.removeSocket(socket);
+  });
+}
+util.inherits(TunnelingAgent, events.EventEmitter);
+
+TunnelingAgent.prototype.addRequest = function addRequest(req, host, port, localAddress) {
+  var self = this;
+  var options = mergeOptions({request: req}, self.options, toOptions(host, port, localAddress));
+
+  if (self.sockets.length >= this.maxSockets) {
+    // We are over limit so we'll add it to the queue.
+    self.requests.push(options);
+    return;
+  }
+
+  // If we are under maxSockets create a new one.
+  self.createSocket(options, function(socket) {
+    socket.on('free', onFree);
+    socket.on('close', onCloseOrRemove);
+    socket.on('agentRemove', onCloseOrRemove);
+    req.onSocket(socket);
+
+    function onFree() {
+      self.emit('free', socket, options);
+    }
+
+    function onCloseOrRemove(err) {
+      self.removeSocket(socket);
+      socket.removeListener('free', onFree);
+      socket.removeListener('close', onCloseOrRemove);
+      socket.removeListener('agentRemove', onCloseOrRemove);
+    }
+  });
+};
+
+TunnelingAgent.prototype.createSocket = function createSocket(options, cb) {
+  var self = this;
+  var placeholder = {};
+  self.sockets.push(placeholder);
+
+  var connectOptions = mergeOptions({}, self.proxyOptions, {
+    method: 'CONNECT',
+    path: options.host + ':' + options.port,
+    agent: false,
+    headers: {
+      host: options.host + ':' + options.port
+    }
+  });
+  if (options.localAddress) {
+    connectOptions.localAddress = options.localAddress;
+  }
+  if (connectOptions.proxyAuth) {
+    connectOptions.headers = connectOptions.headers || {};
+    connectOptions.headers['Proxy-Authorization'] = 'Basic ' +
+        new Buffer(connectOptions.proxyAuth).toString('base64');
+  }
+
+  debug('making CONNECT request');
+  var connectReq = self.request(connectOptions);
+  connectReq.useChunkedEncodingByDefault = false; // for v0.6
+  connectReq.once('response', onResponse); // for v0.6
+  connectReq.once('upgrade', onUpgrade);   // for v0.6
+  connectReq.once('connect', onConnect);   // for v0.7 or later
+  connectReq.once('error', onError);
+  connectReq.end();
+
+  function onResponse(res) {
+    // Very hacky. This is necessary to avoid http-parser leaks.
+    res.upgrade = true;
+  }
+
+  function onUpgrade(res, socket, head) {
+    // Hacky.
+    process.nextTick(function() {
+      onConnect(res, socket, head);
+    });
+  }
+
+  function onConnect(res, socket, head) {
+    connectReq.removeAllListeners();
+    socket.removeAllListeners();
+
+    if (res.statusCode !== 200) {
+      debug('tunneling socket could not be established, statusCode=%d',
+        res.statusCode);
+      socket.destroy();
+      var error = new Error('tunneling socket could not be established, ' +
+        'statusCode=' + res.statusCode);
+      error.code = 'ECONNRESET';
+      options.request.emit('error', error);
+      self.removeSocket(placeholder);
+      return;
+    }
+    if (head.length > 0) {
+      debug('got illegal response body from proxy');
+      socket.destroy();
+      var error = new Error('got illegal response body from proxy');
+      error.code = 'ECONNRESET';
+      options.request.emit('error', error);
+      self.removeSocket(placeholder);
+      return;
+    }
+    debug('tunneling connection has established');
+    self.sockets[self.sockets.indexOf(placeholder)] = socket;
+    return cb(socket);
+  }
+
+  function onError(cause) {
+    connectReq.removeAllListeners();
+
+    debug('tunneling socket could not be established, cause=%s\n',
+          cause.message, cause.stack);
+    var error = new Error('tunneling socket could not be established, ' +
+                          'cause=' + cause.message);
+    error.code = 'ECONNRESET';
+    options.request.emit('error', error);
+    self.removeSocket(placeholder);
+  }
+};
+
+TunnelingAgent.prototype.removeSocket = function removeSocket(socket) {
+  var pos = this.sockets.indexOf(socket)
+  if (pos === -1) {
+    return;
+  }
+  this.sockets.splice(pos, 1);
+
+  var pending = this.requests.shift();
+  if (pending) {
+    // If we have pending requests and a socket gets closed a new one
+    // needs to be created to take over in the pool for the one that closed.
+    this.createSocket(pending, function(socket) {
+      pending.request.onSocket(socket);
+    });
+  }
+};
+
+function createSecureSocket(options, cb) {
+  var self = this;
+  TunnelingAgent.prototype.createSocket.call(self, options, function(socket) {
+    var hostHeader = options.request.getHeader('host');
+    var tlsOptions = mergeOptions({}, self.options, {
+      socket: socket,
+      servername: hostHeader ? hostHeader.replace(/:.*$/, '') : options.host
+    });
+
+    // 0 is dummy port for v0.6
+    var secureSocket = tls.connect(0, tlsOptions);
+    self.sockets[self.sockets.indexOf(socket)] = secureSocket;
+    cb(secureSocket);
+  });
+}
+
+
+function toOptions(host, port, localAddress) {
+  if (typeof host === 'string') { // since v0.10
+    return {
+      host: host,
+      port: port,
+      localAddress: localAddress
+    };
+  }
+  return host; // for v0.11 or later
+}
+
+function mergeOptions(target) {
+  for (var i = 1, len = arguments.length; i < len; ++i) {
+    var overrides = arguments[i];
+    if (typeof overrides === 'object') {
+      var keys = Object.keys(overrides);
+      for (var j = 0, keyLen = keys.length; j < keyLen; ++j) {
+        var k = keys[j];
+        if (overrides[k] !== undefined) {
+          target[k] = overrides[k];
+        }
+      }
+    }
+  }
+  return target;
+}
+
+
+var debug;
+if (process.env.NODE_DEBUG && /\btunnel\b/.test(process.env.NODE_DEBUG)) {
+  debug = function() {
+    var args = Array.prototype.slice.call(arguments);
+    if (typeof args[0] === 'string') {
+      args[0] = 'TUNNEL: ' + args[0];
+    } else {
+      args.unshift('TUNNEL:');
+    }
+    console.error.apply(console, args);
+  }
+} else {
+  debug = function() {};
+}
+exports.debug = debug; // for test
+
+
+/***/ }),
+
+/***/ 143:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = withAuthorizationPrefix;
+
+const atob = __webpack_require__(368);
+
+const REGEX_IS_BASIC_AUTH = /^[\w-]+:/;
+
+function withAuthorizationPrefix(authorization) {
+  if (/^(basic|bearer|token) /i.test(authorization)) {
+    return authorization;
+  }
+
+  try {
+    if (REGEX_IS_BASIC_AUTH.test(atob(authorization))) {
+      return `basic ${authorization}`;
+    }
+  } catch (error) {}
+
+  if (authorization.split(/\./).length === 3) {
+    return `bearer ${authorization}`;
+  }
+
+  return `token ${authorization}`;
+}
+
+
+/***/ }),
+
+/***/ 145:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+const pump = __webpack_require__(453);
+const bufferStream = __webpack_require__(966);
+
+class MaxBufferError extends Error {
+	constructor() {
+		super('maxBuffer exceeded');
+		this.name = 'MaxBufferError';
+	}
+}
+
+function getStream(inputStream, options) {
+	if (!inputStream) {
+		return Promise.reject(new Error('Expected a stream'));
+	}
+
+	options = Object.assign({maxBuffer: Infinity}, options);
+
+	const {maxBuffer} = options;
+
+	let stream;
+	return new Promise((resolve, reject) => {
+		const rejectPromise = error => {
+			if (error) { // A null check
+				error.bufferedData = stream.getBufferedValue();
+			}
+			reject(error);
+		};
+
+		stream = pump(inputStream, bufferStream(options), error => {
+			if (error) {
+				rejectPromise(error);
+				return;
+			}
+
+			resolve();
+		});
+
+		stream.on('data', () => {
+			if (stream.getBufferedLength() > maxBuffer) {
+				rejectPromise(new MaxBufferError());
+			}
+		});
+	}).then(() => stream.getBufferedValue());
+}
+
+module.exports = getStream;
+module.exports.buffer = (stream, options) => getStream(stream, Object.assign({}, options, {encoding: 'buffer'}));
+module.exports.array = (stream, options) => getStream(stream, Object.assign({}, options, {array: true}));
+module.exports.MaxBufferError = MaxBufferError;
+
+
+/***/ }),
+
+/***/ 148:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = paginatePlugin;
+
+const { paginateRest } = __webpack_require__(299);
+
+function paginatePlugin(octokit) {
+  Object.assign(octokit, paginateRest(octokit));
+}
+
+
+/***/ }),
+
+/***/ 168:
+/***/ (function(module) {
+
+"use strict";
+
+const alias = ['stdin', 'stdout', 'stderr'];
+
+const hasAlias = opts => alias.some(x => Boolean(opts[x]));
+
+module.exports = opts => {
+	if (!opts) {
+		return null;
+	}
+
+	if (opts.stdio && hasAlias(opts)) {
+		throw new Error(`It's not possible to provide \`stdio\` in combination with one of ${alias.map(x => `\`${x}\``).join(', ')}`);
+	}
+
+	if (typeof opts.stdio === 'string') {
+		return opts.stdio;
+	}
+
+	const stdio = opts.stdio || [];
+
+	if (!Array.isArray(stdio)) {
+		throw new TypeError(`Expected \`stdio\` to be of type \`string\` or \`Array\`, got \`${typeof stdio}\``);
+	}
+
+	const result = [];
+	const len = Math.max(stdio.length, alias.length);
+
+	for (let i = 0; i < len; i++) {
+		let value = null;
+
+		if (stdio[i] !== undefined) {
+			value = stdio[i];
+		} else if (opts[alias[i]] !== undefined) {
+			value = opts[alias[i]];
+		}
+
+		result[i] = value;
+	}
+
+	return result;
+};
+
+
+/***/ }),
+
+/***/ 190:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticationPlugin;
+
+const { createTokenAuth } = __webpack_require__(813);
+const { Deprecation } = __webpack_require__(692);
+const once = __webpack_require__(969);
+
+const beforeRequest = __webpack_require__(863);
+const requestError = __webpack_require__(293);
+const validate = __webpack_require__(954);
+const withAuthorizationPrefix = __webpack_require__(143);
+
+const deprecateAuthBasic = once((log, deprecation) => log.warn(deprecation));
+const deprecateAuthObject = once((log, deprecation) => log.warn(deprecation));
+
+function authenticationPlugin(octokit, options) {
+  // If `options.authStrategy` is set then use it and pass in `options.auth`
+  if (options.authStrategy) {
+    const auth = options.authStrategy(options.auth);
+    octokit.hook.wrap("request", auth.hook);
+    octokit.auth = auth;
+    return;
+  }
+
+  // If neither `options.authStrategy` nor `options.auth` are set, the `octokit` instance
+  // is unauthenticated. The `octokit.auth()` method is a no-op and no request hook is registred.
+  if (!options.auth) {
+    octokit.auth = () =>
+      Promise.resolve({
+        type: "unauthenticated"
+      });
+    return;
+  }
+
+  const isBasicAuthString =
+    typeof options.auth === "string" &&
+    /^basic/.test(withAuthorizationPrefix(options.auth));
+
+  // If only `options.auth` is set to a string, use the default token authentication strategy.
+  if (typeof options.auth === "string" && !isBasicAuthString) {
+    const auth = createTokenAuth(options.auth);
+    octokit.hook.wrap("request", auth.hook);
+    octokit.auth = auth;
+    return;
+  }
+
+  // Otherwise log a deprecation message
+  const [deprecationMethod, deprecationMessapge] = isBasicAuthString
+    ? [
+        deprecateAuthBasic,
+        'Setting the "new Octokit({ auth })" option to a Basic Auth string is deprecated. Use https://github.com/octokit/auth-basic.js instead. See (https://octokit.github.io/rest.js/#authentication)'
+      ]
+    : [
+        deprecateAuthObject,
+        'Setting the "new Octokit({ auth })" option to an object without also setting the "authStrategy" option is deprecated and will be removed in v17. See (https://octokit.github.io/rest.js/#authentication)'
+      ];
+  deprecationMethod(
+    octokit.log,
+    new Deprecation("[@octokit/rest] " + deprecationMessapge)
+  );
+
+  octokit.auth = () =>
+    Promise.resolve({
+      type: "deprecated",
+      message: deprecationMessapge
+    });
+
+  validate(options.auth);
+
+  const state = {
+    octokit,
+    auth: options.auth
+  };
+
+  octokit.hook.before("request", beforeRequest.bind(null, state));
+  octokit.hook.error("request", requestError.bind(null, state));
+}
+
+
+/***/ }),
+
+/***/ 197:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = isexe
+isexe.sync = sync
+
+var fs = __webpack_require__(747)
+
+function isexe (path, options, cb) {
+  fs.stat(path, function (er, stat) {
+    cb(er, er ? false : checkStat(stat, options))
+  })
+}
+
+function sync (path, options) {
+  return checkStat(fs.statSync(path), options)
+}
+
+function checkStat (stat, options) {
+  return stat.isFile() && checkMode(stat, options)
+}
+
+function checkMode (stat, options) {
+  var mod = stat.mode
+  var uid = stat.uid
+  var gid = stat.gid
+
+  var myUid = options.uid !== undefined ?
+    options.uid : process.getuid && process.getuid()
+  var myGid = options.gid !== undefined ?
+    options.gid : process.getgid && process.getgid()
+
+  var u = parseInt('100', 8)
+  var g = parseInt('010', 8)
+  var o = parseInt('001', 8)
+  var ug = u | g
+
+  var ret = (mod & o) ||
+    (mod & g) && gid === myGid ||
+    (mod & u) && uid === myUid ||
+    (mod & ug) && myUid === 0
+
+  return ret
+}
+
+
+/***/ }),
+
+/***/ 211:
+/***/ (function(module) {
+
+module.exports = require("https");
+
+/***/ }),
+
+/***/ 215:
+/***/ (function(module) {
+
+module.exports = {"_args":[["@octokit/rest@16.43.0","/Users/jason/devel/typescript-action"]],"_from":"@octokit/rest@16.43.0","_id":"@octokit/rest@16.43.0","_inBundle":false,"_integrity":"sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==","_location":"/@octokit/rest","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.0","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawSp [...]
+
+/***/ }),
+
+/***/ 260:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+// Note: since nyc uses this module to output coverage, any lines
+// that are in the direct sync flow of nyc's outputCoverage are
+// ignored, since we can never get coverage for them.
+var assert = __webpack_require__(357)
+var signals = __webpack_require__(654)
+
+var EE = __webpack_require__(614)
+/* istanbul ignore if */
+if (typeof EE !== 'function') {
+  EE = EE.EventEmitter
+}
+
+var emitter
+if (process.__signal_exit_emitter__) {
+  emitter = process.__signal_exit_emitter__
+} else {
+  emitter = process.__signal_exit_emitter__ = new EE()
+  emitter.count = 0
+  emitter.emitted = {}
+}
+
+// Because this emitter is a global, we have to check to see if a
+// previous version of this library failed to enable infinite listeners.
+// I know what you're about to say.  But literally everything about
+// signal-exit is a compromise with evil.  Get used to it.
+if (!emitter.infinite) {
+  emitter.setMaxListeners(Infinity)
+  emitter.infinite = true
+}
+
+module.exports = function (cb, opts) {
+  assert.equal(typeof cb, 'function', 'a callback must be provided for exit handler')
+
+  if (loaded === false) {
+    load()
+  }
+
+  var ev = 'exit'
+  if (opts && opts.alwaysLast) {
+    ev = 'afterexit'
+  }
+
+  var remove = function () {
+    emitter.removeListener(ev, cb)
+    if (emitter.listeners('exit').length === 0 &&
+        emitter.listeners('afterexit').length === 0) {
+      unload()
+    }
+  }
+  emitter.on(ev, cb)
+
+  return remove
+}
+
+module.exports.unload = unload
+function unload () {
+  if (!loaded) {
+    return
+  }
+  loaded = false
+
+  signals.forEach(function (sig) {
+    try {
+      process.removeListener(sig, sigListeners[sig])
+    } catch (er) {}
+  })
+  process.emit = originalProcessEmit
+  process.reallyExit = originalProcessReallyExit
+  emitter.count -= 1
+}
+
+function emit (event, code, signal) {
+  if (emitter.emitted[event]) {
+    return
+  }
+  emitter.emitted[event] = true
+  emitter.emit(event, code, signal)
+}
+
+// { <signal>: <listener fn>, ... }
+var sigListeners = {}
+signals.forEach(function (sig) {
+  sigListeners[sig] = function listener () {
+    // If there are no other listeners, an exit is coming!
+    // Simplest way: remove us and then re-send the signal.
+    // We know that this will kill the process, so we can
+    // safely emit now.
+    var listeners = process.listeners(sig)
+    if (listeners.length === emitter.count) {
+      unload()
+      emit('exit', null, sig)
+      /* istanbul ignore next */
+      emit('afterexit', null, sig)
+      /* istanbul ignore next */
+      process.kill(process.pid, sig)
+    }
+  }
+})
+
+module.exports.signals = function () {
+  return signals
+}
+
+module.exports.load = load
+
+var loaded = false
+
+function load () {
+  if (loaded) {
+    return
+  }
+  loaded = true
+
+  // This is the number of onSignalExit's that are in play.
+  // It's important so that we can count the correct number of
+  // listeners on signals, and don't wait for the other one to
+  // handle it instead of us.
+  emitter.count += 1
+
+  signals = signals.filter(function (sig) {
+    try {
+      process.on(sig, sigListeners[sig])
+      return true
+    } catch (er) {
+      return false
+    }
+  })
+
+  process.emit = processEmit
+  process.reallyExit = processReallyExit
+}
+
+var originalProcessReallyExit = process.reallyExit
+function processReallyExit (code) {
+  process.exitCode = code || 0
+  emit('exit', process.exitCode, null)
+  /* istanbul ignore next */
+  emit('afterexit', process.exitCode, null)
+  /* istanbul ignore next */
+  originalProcessReallyExit.call(process, process.exitCode)
+}
+
+var originalProcessEmit = process.emit
+function processEmit (ev, arg) {
+  if (ev === 'exit') {
+    if (arg !== undefined) {
+      process.exitCode = arg
+    }
+    var ret = originalProcessEmit.apply(this, arguments)
+    emit('exit', process.exitCode, null)
+    /* istanbul ignore next */
+    emit('afterexit', process.exitCode, null)
+    return ret
+  } else {
+    return originalProcessEmit.apply(this, arguments)
+  }
+}
+
+
+/***/ }),
+
+/***/ 262:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", { value: true });
+const fs_1 = __webpack_require__(747);
+const os_1 = __webpack_require__(87);
+class Context {
+    /**
+     * Hydrate the context from the environment
+     */
+    constructor() {
+        this.payload = {};
+        if (process.env.GITHUB_EVENT_PATH) {
+            if (fs_1.existsSync(process.env.GITHUB_EVENT_PATH)) {
+                this.payload = JSON.parse(fs_1.readFileSync(process.env.GITHUB_EVENT_PATH, { encoding: 'utf8' }));
+            }
+            else {
+                const path = process.env.GITHUB_EVENT_PATH;
+                process.stdout.write(`GITHUB_EVENT_PATH ${path} does not exist${os_1.EOL}`);
+            }
+        }
+        this.eventName = process.env.GITHUB_EVENT_NAME;
+        this.sha = process.env.GITHUB_SHA;
+        this.ref = process.env.GITHUB_REF;
+        this.workflow = process.env.GITHUB_WORKFLOW;
+        this.action = process.env.GITHUB_ACTION;
+        this.actor = process.env.GITHUB_ACTOR;
+    }
+    get issue() {
+        const payload = this.payload;
+        return Object.assign(Object.assign({}, this.repo), { number: (payload.issue || payload.pull_request || payload).number });
+    }
+    get repo() {
+        if (process.env.GITHUB_REPOSITORY) {
+            const [owner, repo] = process.env.GITHUB_REPOSITORY.split('/');
+            return { owner, repo };
+        }
+        if (this.payload.repository) {
+            return {
+                owner: this.payload.repository.owner.login,
+                repo: this.payload.repository.name
+            };
+        }
+        throw new Error("context.repo requires a GITHUB_REPOSITORY environment variable like 'owner/repo'");
+    }
+}
+exports.Context = Context;
+//# sourceMappingURL=context.js.map
+
+/***/ }),
+
+/***/ 265:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = getPage
+
+const deprecate = __webpack_require__(370)
+const getPageLinks = __webpack_require__(577)
+const HttpError = __webpack_require__(297)
+
+function getPage (octokit, link, which, headers) {
+  deprecate(`octokit.get${which.charAt(0).toUpperCase() + which.slice(1)}Page() – You can use octokit.paginate or async iterators instead: https://github.com/octokit/rest.js#pagination.`)
+  const url = getPageLinks(link)[which]
+
+  if (!url) {
+    const urlError = new HttpError(`No ${which} page found`, 404)
+    return Promise.reject(urlError)
+  }
+
+  const requestOptions = {
+    url,
+    headers: applyAcceptHeader(link, headers)
+  }
+
+  const promise = octokit.request(requestOptions)
+
+  return promise
+}
+
+function applyAcceptHeader (res, headers) {
+  const previous = res.headers && res.headers['x-github-media-type']
+
+  if (!previous || (headers && headers.accept)) {
+    return headers
+  }
+  headers = headers || {}
+  headers.accept = 'application/vnd.' + previous
+    .replace('; param=', '.')
+    .replace('; format=', '+')
+
+  return headers
+}
+
+
+/***/ }),
+
+/***/ 280:
+/***/ (function(module) {
+
+module.exports = register
+
+function register (state, name, method, options) {
+  if (typeof method !== 'function') {
+    throw new Error('method for before hook must be a function')
+  }
+
+  if (!options) {
+    options = {}
+  }
+
+  if (Array.isArray(name)) {
+    return name.reverse().reduce(function (callback, name) {
+      return register.bind(null, state, name, callback, options)
+    }, method)()
+  }
+
+  return Promise.resolve()
+    .then(function () {
+      if (!state.registry[name]) {
+        return method(options)
+      }
+
+      return (state.registry[name]).reduce(function (method, registered) {
+        return registered.hook.bind(null, method, options)
+      }, method)()
+    })
+}
+
+
+/***/ }),
+
+/***/ 293:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticationRequestError;
+
+const { RequestError } = __webpack_require__(463);
+
+function authenticationRequestError(state, error, options) {
+  if (!error.headers) throw error;
+
+  const otpRequired = /required/.test(error.headers["x-github-otp"] || "");
+  // handle "2FA required" error only
+  if (error.status !== 401 || !otpRequired) {
+    throw error;
+  }
+
+  if (
+    error.status === 401 &&
+    otpRequired &&
+    error.request &&
+    error.request.headers["x-github-otp"]
+  ) {
+    if (state.otp) {
+      delete state.otp; // no longer valid, request again
+    } else {
+      throw new RequestError(
+        "Invalid one-time password for two-factor authentication",
+        401,
+        {
+          headers: error.headers,
+          request: options
+        }
+      );
+    }
+  }
+
+  if (typeof state.auth.on2fa !== "function") {
+    throw new RequestError(
+      "2FA required, but options.on2fa is not a function. See https://github.com/octokit/rest.js#authentication",
+      401,
+      {
+        headers: error.headers,
+        request: options
+      }
+    );
+  }
+
+  return Promise.resolve()
+    .then(() => {
+      return state.auth.on2fa();
+    })
+    .then(oneTimePassword => {
+      const newOptions = Object.assign(options, {
+        headers: Object.assign(options.headers, {
+          "x-github-otp": oneTimePassword
+        })
+      });
+      return state.octokit.request(newOptions).then(response => {
+        // If OTP still valid, then persist it for following requests
+        state.otp = oneTimePassword;
+        return response;
+      });
+    });
+}
+
+
+/***/ }),
+
+/***/ 294:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = parseOptions;
+
+const { Deprecation } = __webpack_require__(692);
+const { getUserAgent } = __webpack_require__(796);
+const once = __webpack_require__(969);
+
+const pkg = __webpack_require__(215);
+
+const deprecateOptionsTimeout = once((log, deprecation) =>
+  log.warn(deprecation)
+);
+const deprecateOptionsAgent = once((log, deprecation) => log.warn(deprecation));
+const deprecateOptionsHeaders = once((log, deprecation) =>
+  log.warn(deprecation)
+);
+
+function parseOptions(options, log, hook) {
+  if (options.headers) {
+    options.headers = Object.keys(options.headers).reduce((newObj, key) => {
+      newObj[key.toLowerCase()] = options.headers[key];
+      return newObj;
+    }, {});
+  }
+
+  const clientDefaults = {
+    headers: options.headers || {},
+    request: options.request || {},
+    mediaType: {
+      previews: [],
+      format: ""
+    }
+  };
+
+  if (options.baseUrl) {
+    clientDefaults.baseUrl = options.baseUrl;
+  }
+
+  if (options.userAgent) {
+    clientDefaults.headers["user-agent"] = options.userAgent;
+  }
+
+  if (options.previews) {
+    clientDefaults.mediaType.previews = options.previews;
+  }
+
+  if (options.timeZone) {
+    clientDefaults.headers["time-zone"] = options.timeZone;
+  }
+
+  if (options.timeout) {
+    deprecateOptionsTimeout(
+      log,
+      new Deprecation(
+        "[@octokit/rest] new Octokit({timeout}) is deprecated. Use {request: {timeout}} instead. See https://github.com/octokit/request.js#request"
+      )
+    );
+    clientDefaults.request.timeout = options.timeout;
+  }
+
+  if (options.agent) {
+    deprecateOptionsAgent(
+      log,
+      new Deprecation(
+        "[@octokit/rest] new Octokit({agent}) is deprecated. Use {request: {agent}} instead. See https://github.com/octokit/request.js#request"
+      )
+    );
+    clientDefaults.request.agent = options.agent;
+  }
+
+  if (options.headers) {
+    deprecateOptionsHeaders(
+      log,
+      new Deprecation(
+        "[@octokit/rest] new Octokit({headers}) is deprecated. Use {userAgent, previews} instead. See https://github.com/octokit/request.js#request"
+      )
+    );
+  }
+
+  const userAgentOption = clientDefaults.headers["user-agent"];
+  const defaultUserAgent = `octokit.js/${pkg.version} ${getUserAgent()}`;
+
+  clientDefaults.headers["user-agent"] = [userAgentOption, defaultUserAgent]
+    .filter(Boolean)
+    .join(" ");
+
+  clientDefaults.request.hook = hook.bind(null, "request");
+
+  return clientDefaults;
+}
+
+
+/***/ }),
+
+/***/ 297:
+/***/ (function(module) {
+
+module.exports = class HttpError extends Error {
+  constructor (message, code, headers) {
+    super(message)
+
+    // Maintains proper stack trace (only available on V8)
+    /* istanbul ignore next */
+    if (Error.captureStackTrace) {
+      Error.captureStackTrace(this, this.constructor)
+    }
+
+    this.name = 'HttpError'
+    this.code = code
+    this.headers = headers
+  }
+}
+
+
+/***/ }),
+
+/***/ 299:
+/***/ (function(__unusedmodule, exports) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+const VERSION = "1.1.2";
+
+/**
+ * Some “list” response that can be paginated have a different response structure
+ *
+ * They have a `total_count` key in the response (search also has `incomplete_results`,
+ * /installation/repositories also has `repository_selection`), as well as a key with
+ * the list of the items which name varies from endpoint to endpoint:
+ *
+ * - https://developer.github.com/v3/search/#example (key `items`)
+ * - https://developer.github.com/v3/checks/runs/#response-3 (key: `check_runs`)
+ * - https://developer.github.com/v3/checks/suites/#response-1 (key: `check_suites`)
+ * - https://developer.github.com/v3/apps/installations/#list-repositories (key: `repositories`)
+ * - https://developer.github.com/v3/apps/installations/#list-installations-for-a-user (key `installations`)
+ *
+ * Octokit normalizes these responses so that paginated results are always returned following
+ * the same structure. One challenge is that if the list response has only one page, no Link
+ * header is provided, so this header alone is not sufficient to check wether a response is
+ * paginated or not. For the exceptions with the namespace, a fallback check for the route
+ * paths has to be added in order to normalize the response. We cannot check for the total_count
+ * property because it also exists in the response of Get the combined status for a specific ref.
+ */
+const REGEX = [/^\/search\//, /^\/repos\/[^/]+\/[^/]+\/commits\/[^/]+\/(check-runs|check-suites)([^/]|$)/, /^\/installation\/repositories([^/]|$)/, /^\/user\/installations([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/secrets([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/workflows(\/[^/]+\/runs)?([^/]|$)/, /^\/repos\/[^/]+\/[^/]+\/actions\/runs(\/[^/]+\/(artifacts|jobs))?([^/]|$)/];
+function normalizePaginatedListResponse(octokit, url, response) {
+  const path = url.replace(octokit.request.endpoint.DEFAULTS.baseUrl, "");
+  const responseNeedsNormalization = REGEX.find(regex => regex.test(path));
+  if (!responseNeedsNormalization) return; // keep the additional properties intact as there is currently no other way
+  // to retrieve the same information.
+
+  const incompleteResults = response.data.incomplete_results;
+  const repositorySelection = response.data.repository_selection;
+  const totalCount = response.data.total_count;
+  delete response.data.incomplete_results;
+  delete response.data.repository_selection;
+  delete response.data.total_count;
+  const namespaceKey = Object.keys(response.data)[0];
+  const data = response.data[namespaceKey];
+  response.data = data;
+
+  if (typeof incompleteResults !== "undefined") {
+    response.data.incomplete_results = incompleteResults;
+  }
+
+  if (typeof repositorySelection !== "undefined") {
+    response.data.repository_selection = repositorySelection;
+  }
+
+  response.data.total_count = totalCount;
+  Object.defineProperty(response.data, namespaceKey, {
+    get() {
+      octokit.log.warn(`[@octokit/paginate-rest] "response.data.${namespaceKey}" is deprecated for "GET ${path}". Get the results directly from "response.data"`);
+      return Array.from(data);
+    }
+
+  });
+}
+
+function iterator(octokit, route, parameters) {
+  const options = octokit.request.endpoint(route, parameters);
+  const method = options.method;
+  const headers = options.headers;
+  let url = options.url;
+  return {
+    [Symbol.asyncIterator]: () => ({
+      next() {
+        if (!url) {
+          return Promise.resolve({
+            done: true
+          });
+        }
+
+        return octokit.request({
+          method,
+          url,
+          headers
+        }).then(response => {
+          normalizePaginatedListResponse(octokit, url, response); // `response.headers.link` format:
+          // '<https://api.github.com/users/aseemk/followers?page=2>; rel="next", <https://api.github.com/users/aseemk/followers?page=2>; rel="last"'
+          // sets `url` to undefined if "next" URL is not present or `link` header is not set
+
+          url = ((response.headers.link || "").match(/<([^>]+)>;\s*rel="next"/) || [])[1];
+          return {
+            value: response
+          };
+        });
+      }
+
+    })
+  };
+}
+
+function paginate(octokit, route, parameters, mapFn) {
+  if (typeof parameters === "function") {
+    mapFn = parameters;
+    parameters = undefined;
+  }
+
+  return gather(octokit, [], iterator(octokit, route, parameters)[Symbol.asyncIterator](), mapFn);
+}
+
+function gather(octokit, results, iterator, mapFn) {
+  return iterator.next().then(result => {
+    if (result.done) {
+      return results;
+    }
+
+    let earlyExit = false;
+
+    function done() {
+      earlyExit = true;
+    }
+
+    results = results.concat(mapFn ? mapFn(result.value, done) : result.value.data);
+
+    if (earlyExit) {
+      return results;
+    }
+
+    return gather(octokit, results, iterator, mapFn);
+  });
+}
+
+/**
+ * @param octokit Octokit instance
+ * @param options Options passed to Octokit constructor
+ */
+
+function paginateRest(octokit) {
+  return {
+    paginate: Object.assign(paginate.bind(null, octokit), {
+      iterator: iterator.bind(null, octokit)
+    })
+  };
+}
+paginateRest.VERSION = VERSION;
+
+exports.paginateRest = paginateRest;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 323:
+/***/ (function(module) {
+
+"use strict";
+
+
+var isStream = module.exports = function (stream) {
+	return stream !== null && typeof stream === 'object' && typeof stream.pipe === 'function';
+};
+
+isStream.writable = function (stream) {
+	return isStream(stream) && stream.writable !== false && typeof stream._write === 'function' && typeof stream._writableState === 'object';
+};
+
+isStream.readable = function (stream) {
+	return isStream(stream) && stream.readable !== false && typeof stream._read === 'function' && typeof stream._readableState === 'object';
+};
+
+isStream.duplex = function (stream) {
+	return isStream.writable(stream) && isStream.readable(stream);
+};
+
+isStream.transform = function (stream) {
+	return isStream.duplex(stream) && typeof stream._transform === 'function' && typeof stream._transformState === 'object';
+};
+
+
+/***/ }),
+
+/***/ 336:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = hasLastPage
+
+const deprecate = __webpack_require__(370)
+const getPageLinks = __webpack_require__(577)
+
+function hasLastPage (link) {
+  deprecate(`octokit.hasLastPage() – You can use octokit.paginate or async iterators instead: https://github.com/octokit/rest.js#pagination.`)
+  return getPageLinks(link).last
+}
+
+
+/***/ }),
+
+/***/ 348:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+
+module.exports = validate;
+
+const { RequestError } = __webpack_require__(463);
+const get = __webpack_require__(854);
+const set = __webpack_require__(883);
+
+function validate(octokit, options) {
+  if (!options.request.validate) {
+    return;
+  }
+  const { validate: params } = options.request;
+
+  Object.keys(params).forEach(parameterName => {
+    const parameter = get(params, parameterName);
+
+    const expectedType = parameter.type;
+    let parentParameterName;
+    let parentValue;
+    let parentParamIsPresent = true;
+    let parentParameterIsArray = false;
+
+    if (/\./.test(parameterName)) {
+      parentParameterName = parameterName.replace(/\.[^.]+$/, "");
+      parentParameterIsArray = parentParameterName.slice(-2) === "[]";
+      if (parentParameterIsArray) {
+        parentParameterName = parentParameterName.slice(0, -2);
+      }
+      parentValue = get(options, parentParameterName);
+      parentParamIsPresent =
+        parentParameterName === "headers" ||
+        (typeof parentValue === "object" && parentValue !== null);
+    }
+
+    const values = parentParameterIsArray
+      ? (get(options, parentParameterName) || []).map(
+          value => value[parameterName.split(/\./).pop()]
+        )
+      : [get(options, parameterName)];
+
+    values.forEach((value, i) => {
+      const valueIsPresent = typeof value !== "undefined";
+      const valueIsNull = value === null;
+      const currentParameterName = parentParameterIsArray
+        ? parameterName.replace(/\[\]/, `[${i}]`)
+        : parameterName;
+
+      if (!parameter.required && !valueIsPresent) {
+        return;
+      }
+
+      // if the parent parameter is of type object but allows null
+      // then the child parameters can be ignored
+      if (!parentParamIsPresent) {
+        return;
+      }
+
+      if (parameter.allowNull && valueIsNull) {
+        return;
+      }
+
+      if (!parameter.allowNull && valueIsNull) {
+        throw new RequestError(
+          `'${currentParameterName}' cannot be null`,
+          400,
+          {
+            request: options
+          }
+        );
+      }
+
+      if (parameter.required && !valueIsPresent) {
+        throw new RequestError(
+          `Empty value for parameter '${currentParameterName}': ${JSON.stringify(
+            value
+          )}`,
+          400,
+          {
+            request: options
+          }
+        );
+      }
+
+      // parse to integer before checking for enum
+      // so that string "1" will match enum with number 1
+      if (expectedType === "integer") {
+        const unparsedValue = value;
+        value = parseInt(value, 10);
+        if (isNaN(value)) {
+          throw new RequestError(
+            `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
+              unparsedValue
+            )} is NaN`,
+            400,
+            {
+              request: options
+            }
+          );
+        }
+      }
+
+      if (parameter.enum && parameter.enum.indexOf(String(value)) === -1) {
+        throw new RequestError(
+          `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
+            value
+          )}`,
+          400,
+          {
+            request: options
+          }
+        );
+      }
+
+      if (parameter.validation) {
+        const regex = new RegExp(parameter.validation);
+        if (!regex.test(value)) {
+          throw new RequestError(
+            `Invalid value for parameter '${currentParameterName}': ${JSON.stringify(
+              value
+            )}`,
+            400,
+            {
+              request: options
+            }
+          );
+        }
+      }
+
+      if (expectedType === "object" && typeof value === "string") {
+        try {
+          value = JSON.parse(value);
+        } catch (exception) {
+          throw new RequestError(
+            `JSON parse error of value for parameter '${currentParameterName}': ${JSON.stringify(
+              value
+            )}`,
+            400,
+            {
+              request: options
+            }
+          );
+        }
+      }
+
+      set(options, parameter.mapTo || currentParameterName, value);
+    });
+  });
+
+  return options;
+}
+
+
+/***/ }),
+
+/***/ 349:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticationRequestError;
+
+const { RequestError } = __webpack_require__(463);
+
+function authenticationRequestError(state, error, options) {
+  /* istanbul ignore next */
+  if (!error.headers) throw error;
+
+  const otpRequired = /required/.test(error.headers["x-github-otp"] || "");
+  // handle "2FA required" error only
+  if (error.status !== 401 || !otpRequired) {
+    throw error;
+  }
+
+  if (
+    error.status === 401 &&
+    otpRequired &&
+    error.request &&
+    error.request.headers["x-github-otp"]
+  ) {
+    throw new RequestError(
+      "Invalid one-time password for two-factor authentication",
+      401,
+      {
+        headers: error.headers,
+        request: options
+      }
+    );
+  }
+
+  if (typeof state.auth.on2fa !== "function") {
+    throw new RequestError(
+      "2FA required, but options.on2fa is not a function. See https://github.com/octokit/rest.js#authentication",
+      401,
+      {
+        headers: error.headers,
+        request: options
+      }
+    );
+  }
+
+  return Promise.resolve()
+    .then(() => {
+      return state.auth.on2fa();
+    })
+    .then(oneTimePassword => {
+      const newOptions = Object.assign(options, {
+        headers: Object.assign(
+          { "x-github-otp": oneTimePassword },
+          options.headers
+        )
+      });
+      return state.octokit.request(newOptions);
+    });
+}
+
+
+/***/ }),
+
+/***/ 357:
+/***/ (function(module) {
+
+module.exports = require("assert");
+
+/***/ }),
+
+/***/ 368:
+/***/ (function(module) {
+
+module.exports = function atob(str) {
+  return Buffer.from(str, 'base64').toString('binary')
+}
+
+
+/***/ }),
+
+/***/ 369:
+/***/ (function(module, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var Stream = _interopDefault(__webpack_require__(413));
+var http = _interopDefault(__webpack_require__(605));
+var Url = _interopDefault(__webpack_require__(835));
+var https = _interopDefault(__webpack_require__(211));
+var zlib = _interopDefault(__webpack_require__(761));
+
+// Based on https://github.com/tmpvar/jsdom/blob/aa85b2abf07766ff7bf5c1f6daafb3726f2f2db5/lib/jsdom/living/blob.js
+
+// fix for "Readable" isn't a named export issue
+const Readable = Stream.Readable;
+
+const BUFFER = Symbol('buffer');
+const TYPE = Symbol('type');
+
+class Blob {
+	constructor() {
+		this[TYPE] = '';
+
+		const blobParts = arguments[0];
+		const options = arguments[1];
+
+		const buffers = [];
+		let size = 0;
+
+		if (blobParts) {
+			const a = blobParts;
+			const length = Number(a.length);
+			for (let i = 0; i < length; i++) {
+				const element = a[i];
+				let buffer;
+				if (element instanceof Buffer) {
+					buffer = element;
+				} else if (ArrayBuffer.isView(element)) {
+					buffer = Buffer.from(element.buffer, element.byteOffset, element.byteLength);
+				} else if (element instanceof ArrayBuffer) {
+					buffer = Buffer.from(element);
+				} else if (element instanceof Blob) {
+					buffer = element[BUFFER];
+				} else {
+					buffer = Buffer.from(typeof element === 'string' ? element : String(element));
+				}
+				size += buffer.length;
+				buffers.push(buffer);
+			}
+		}
+
+		this[BUFFER] = Buffer.concat(buffers);
+
+		let type = options && options.type !== undefined && String(options.type).toLowerCase();
+		if (type && !/[^\u0020-\u007E]/.test(type)) {
+			this[TYPE] = type;
+		}
+	}
+	get size() {
+		return this[BUFFER].length;
+	}
+	get type() {
+		return this[TYPE];
+	}
+	text() {
+		return Promise.resolve(this[BUFFER].toString());
+	}
+	arrayBuffer() {
+		const buf = this[BUFFER];
+		const ab = buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
+		return Promise.resolve(ab);
+	}
+	stream() {
+		const readable = new Readable();
+		readable._read = function () {};
+		readable.push(this[BUFFER]);
+		readable.push(null);
+		return readable;
+	}
+	toString() {
+		return '[object Blob]';
+	}
+	slice() {
+		const size = this.size;
+
+		const start = arguments[0];
+		const end = arguments[1];
+		let relativeStart, relativeEnd;
+		if (start === undefined) {
+			relativeStart = 0;
+		} else if (start < 0) {
+			relativeStart = Math.max(size + start, 0);
+		} else {
+			relativeStart = Math.min(start, size);
+		}
+		if (end === undefined) {
+			relativeEnd = size;
+		} else if (end < 0) {
+			relativeEnd = Math.max(size + end, 0);
+		} else {
+			relativeEnd = Math.min(end, size);
+		}
+		const span = Math.max(relativeEnd - relativeStart, 0);
+
+		const buffer = this[BUFFER];
+		const slicedBuffer = buffer.slice(relativeStart, relativeStart + span);
+		const blob = new Blob([], { type: arguments[2] });
+		blob[BUFFER] = slicedBuffer;
+		return blob;
+	}
+}
+
+Object.defineProperties(Blob.prototype, {
+	size: { enumerable: true },
+	type: { enumerable: true },
+	slice: { enumerable: true }
+});
+
+Object.defineProperty(Blob.prototype, Symbol.toStringTag, {
+	value: 'Blob',
+	writable: false,
+	enumerable: false,
+	configurable: true
+});
+
+/**
+ * fetch-error.js
+ *
+ * FetchError interface for operational errors
+ */
+
+/**
+ * Create FetchError instance
+ *
+ * @param   String      message      Error message for human
+ * @param   String      type         Error type for machine
+ * @param   String      systemError  For Node.js system error
+ * @return  FetchError
+ */
+function FetchError(message, type, systemError) {
+  Error.call(this, message);
+
+  this.message = message;
+  this.type = type;
+
+  // when err.type is `system`, err.code contains system error code
+  if (systemError) {
+    this.code = this.errno = systemError.code;
+  }
+
+  // hide custom error implementation details from end-users
+  Error.captureStackTrace(this, this.constructor);
+}
+
+FetchError.prototype = Object.create(Error.prototype);
+FetchError.prototype.constructor = FetchError;
+FetchError.prototype.name = 'FetchError';
+
+let convert;
+try {
+	convert = __webpack_require__(18).convert;
+} catch (e) {}
+
+const INTERNALS = Symbol('Body internals');
+
+// fix an issue where "PassThrough" isn't a named export for node <10
+const PassThrough = Stream.PassThrough;
+
+/**
+ * Body mixin
+ *
+ * Ref: https://fetch.spec.whatwg.org/#body
+ *
+ * @param   Stream  body  Readable stream
+ * @param   Object  opts  Response options
+ * @return  Void
+ */
+function Body(body) {
+	var _this = this;
+
+	var _ref = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {},
+	    _ref$size = _ref.size;
+
+	let size = _ref$size === undefined ? 0 : _ref$size;
+	var _ref$timeout = _ref.timeout;
+	let timeout = _ref$timeout === undefined ? 0 : _ref$timeout;
+
+	if (body == null) {
+		// body is undefined or null
+		body = null;
+	} else if (isURLSearchParams(body)) {
+		// body is a URLSearchParams
+		body = Buffer.from(body.toString());
+	} else if (isBlob(body)) ; else if (Buffer.isBuffer(body)) ; else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
+		// body is ArrayBuffer
+		body = Buffer.from(body);
+	} else if (ArrayBuffer.isView(body)) {
+		// body is ArrayBufferView
+		body = Buffer.from(body.buffer, body.byteOffset, body.byteLength);
+	} else if (body instanceof Stream) ; else {
+		// none of the above
+		// coerce to string then buffer
+		body = Buffer.from(String(body));
+	}
+	this[INTERNALS] = {
+		body,
+		disturbed: false,
+		error: null
+	};
+	this.size = size;
+	this.timeout = timeout;
+
+	if (body instanceof Stream) {
+		body.on('error', function (err) {
+			const error = err.name === 'AbortError' ? err : new FetchError(`Invalid response body while trying to fetch ${_this.url}: ${err.message}`, 'system', err);
+			_this[INTERNALS].error = error;
+		});
+	}
+}
+
+Body.prototype = {
+	get body() {
+		return this[INTERNALS].body;
+	},
+
+	get bodyUsed() {
+		return this[INTERNALS].disturbed;
+	},
+
+	/**
+  * Decode response as ArrayBuffer
+  *
+  * @return  Promise
+  */
+	arrayBuffer() {
+		return consumeBody.call(this).then(function (buf) {
+			return buf.buffer.slice(buf.byteOffset, buf.byteOffset + buf.byteLength);
+		});
+	},
+
+	/**
+  * Return raw response as Blob
+  *
+  * @return Promise
+  */
+	blob() {
+		let ct = this.headers && this.headers.get('content-type') || '';
+		return consumeBody.call(this).then(function (buf) {
+			return Object.assign(
+			// Prevent copying
+			new Blob([], {
+				type: ct.toLowerCase()
+			}), {
+				[BUFFER]: buf
+			});
+		});
+	},
+
+	/**
+  * Decode response as json
+  *
+  * @return  Promise
+  */
+	json() {
+		var _this2 = this;
+
+		return consumeBody.call(this).then(function (buffer) {
+			try {
+				return JSON.parse(buffer.toString());
+			} catch (err) {
+				return Body.Promise.reject(new FetchError(`invalid json response body at ${_this2.url} reason: ${err.message}`, 'invalid-json'));
+			}
+		});
+	},
+
+	/**
+  * Decode response as text
+  *
+  * @return  Promise
+  */
+	text() {
+		return consumeBody.call(this).then(function (buffer) {
+			return buffer.toString();
+		});
+	},
+
+	/**
+  * Decode response as buffer (non-spec api)
+  *
+  * @return  Promise
+  */
+	buffer() {
+		return consumeBody.call(this);
+	},
+
+	/**
+  * Decode response as text, while automatically detecting the encoding and
+  * trying to decode to UTF-8 (non-spec api)
+  *
+  * @return  Promise
+  */
+	textConverted() {
+		var _this3 = this;
+
+		return consumeBody.call(this).then(function (buffer) {
+			return convertBody(buffer, _this3.headers);
+		});
+	}
+};
+
+// In browsers, all properties are enumerable.
+Object.defineProperties(Body.prototype, {
+	body: { enumerable: true },
+	bodyUsed: { enumerable: true },
+	arrayBuffer: { enumerable: true },
+	blob: { enumerable: true },
+	json: { enumerable: true },
+	text: { enumerable: true }
+});
+
+Body.mixIn = function (proto) {
+	for (const name of Object.getOwnPropertyNames(Body.prototype)) {
+		// istanbul ignore else: future proof
+		if (!(name in proto)) {
+			const desc = Object.getOwnPropertyDescriptor(Body.prototype, name);
+			Object.defineProperty(proto, name, desc);
+		}
+	}
+};
+
+/**
+ * Consume and convert an entire Body to a Buffer.
+ *
+ * Ref: https://fetch.spec.whatwg.org/#concept-body-consume-body
+ *
+ * @return  Promise
+ */
+function consumeBody() {
+	var _this4 = this;
+
+	if (this[INTERNALS].disturbed) {
+		return Body.Promise.reject(new TypeError(`body used already for: ${this.url}`));
+	}
+
+	this[INTERNALS].disturbed = true;
+
+	if (this[INTERNALS].error) {
+		return Body.Promise.reject(this[INTERNALS].error);
+	}
+
+	let body = this.body;
+
+	// body is null
+	if (body === null) {
+		return Body.Promise.resolve(Buffer.alloc(0));
+	}
+
+	// body is blob
+	if (isBlob(body)) {
+		body = body.stream();
+	}
+
+	// body is buffer
+	if (Buffer.isBuffer(body)) {
+		return Body.Promise.resolve(body);
+	}
+
+	// istanbul ignore if: should never happen
+	if (!(body instanceof Stream)) {
+		return Body.Promise.resolve(Buffer.alloc(0));
+	}
+
+	// body is stream
+	// get ready to actually consume the body
+	let accum = [];
+	let accumBytes = 0;
+	let abort = false;
+
+	return new Body.Promise(function (resolve, reject) {
+		let resTimeout;
+
+		// allow timeout on slow response body
+		if (_this4.timeout) {
+			resTimeout = setTimeout(function () {
+				abort = true;
+				reject(new FetchError(`Response timeout while trying to fetch ${_this4.url} (over ${_this4.timeout}ms)`, 'body-timeout'));
+			}, _this4.timeout);
+		}
+
+		// handle stream errors
+		body.on('error', function (err) {
+			if (err.name === 'AbortError') {
+				// if the request was aborted, reject with this Error
+				abort = true;
+				reject(err);
+			} else {
+				// other errors, such as incorrect content-encoding
+				reject(new FetchError(`Invalid response body while trying to fetch ${_this4.url}: ${err.message}`, 'system', err));
+			}
+		});
+
+		body.on('data', function (chunk) {
+			if (abort || chunk === null) {
+				return;
+			}
+
+			if (_this4.size && accumBytes + chunk.length > _this4.size) {
+				abort = true;
+				reject(new FetchError(`content size at ${_this4.url} over limit: ${_this4.size}`, 'max-size'));
+				return;
+			}
+
+			accumBytes += chunk.length;
+			accum.push(chunk);
+		});
+
+		body.on('end', function () {
+			if (abort) {
+				return;
+			}
+
+			clearTimeout(resTimeout);
+
+			try {
+				resolve(Buffer.concat(accum, accumBytes));
+			} catch (err) {
+				// handle streams that have accumulated too much data (issue #414)
+				reject(new FetchError(`Could not create Buffer from response body for ${_this4.url}: ${err.message}`, 'system', err));
+			}
+		});
+	});
+}
+
+/**
+ * Detect buffer encoding and convert to target encoding
+ * ref: http://www.w3.org/TR/2011/WD-html5-20110113/parsing.html#determining-the-character-encoding
+ *
+ * @param   Buffer  buffer    Incoming buffer
+ * @param   String  encoding  Target encoding
+ * @return  String
+ */
+function convertBody(buffer, headers) {
+	if (typeof convert !== 'function') {
+		throw new Error('The package `encoding` must be installed to use the textConverted() function');
+	}
+
+	const ct = headers.get('content-type');
+	let charset = 'utf-8';
+	let res, str;
+
+	// header
+	if (ct) {
+		res = /charset=([^;]*)/i.exec(ct);
+	}
+
+	// no charset in content type, peek at response body for at most 1024 bytes
+	str = buffer.slice(0, 1024).toString();
+
+	// html5
+	if (!res && str) {
+		res = /<meta.+?charset=(['"])(.+?)\1/i.exec(str);
+	}
+
+	// html4
+	if (!res && str) {
+		res = /<meta[\s]+?http-equiv=(['"])content-type\1[\s]+?content=(['"])(.+?)\2/i.exec(str);
+
+		if (res) {
+			res = /charset=(.*)/i.exec(res.pop());
+		}
+	}
+
+	// xml
+	if (!res && str) {
+		res = /<\?xml.+?encoding=(['"])(.+?)\1/i.exec(str);
+	}
+
+	// found charset
+	if (res) {
+		charset = res.pop();
+
+		// prevent decode issues when sites use incorrect encoding
+		// ref: https://hsivonen.fi/encoding-menu/
+		if (charset === 'gb2312' || charset === 'gbk') {
+			charset = 'gb18030';
+		}
+	}
+
+	// turn raw buffers into a single utf-8 buffer
+	return convert(buffer, 'UTF-8', charset).toString();
+}
+
+/**
+ * Detect a URLSearchParams object
+ * ref: https://github.com/bitinn/node-fetch/issues/296#issuecomment-307598143
+ *
+ * @param   Object  obj     Object to detect by type or brand
+ * @return  String
+ */
+function isURLSearchParams(obj) {
+	// Duck-typing as a necessary condition.
+	if (typeof obj !== 'object' || typeof obj.append !== 'function' || typeof obj.delete !== 'function' || typeof obj.get !== 'function' || typeof obj.getAll !== 'function' || typeof obj.has !== 'function' || typeof obj.set !== 'function') {
+		return false;
+	}
+
+	// Brand-checking and more duck-typing as optional condition.
+	return obj.constructor.name === 'URLSearchParams' || Object.prototype.toString.call(obj) === '[object URLSearchParams]' || typeof obj.sort === 'function';
+}
+
+/**
+ * Check if `obj` is a W3C `Blob` object (which `File` inherits from)
+ * @param  {*} obj
+ * @return {boolean}
+ */
+function isBlob(obj) {
+	return typeof obj === 'object' && typeof obj.arrayBuffer === 'function' && typeof obj.type === 'string' && typeof obj.stream === 'function' && typeof obj.constructor === 'function' && typeof obj.constructor.name === 'string' && /^(Blob|File)$/.test(obj.constructor.name) && /^(Blob|File)$/.test(obj[Symbol.toStringTag]);
+}
+
+/**
+ * Clone body given Res/Req instance
+ *
+ * @param   Mixed  instance  Response or Request instance
+ * @return  Mixed
+ */
+function clone(instance) {
+	let p1, p2;
+	let body = instance.body;
+
+	// don't allow cloning a used body
+	if (instance.bodyUsed) {
+		throw new Error('cannot clone body after it is used');
+	}
+
+	// check that body is a stream and not form-data object
+	// note: we can't clone the form-data object without having it as a dependency
+	if (body instanceof Stream && typeof body.getBoundary !== 'function') {
+		// tee instance body
+		p1 = new PassThrough();
+		p2 = new PassThrough();
+		body.pipe(p1);
+		body.pipe(p2);
+		// set instance body to teed body and return the other teed body
+		instance[INTERNALS].body = p1;
+		body = p2;
+	}
+
+	return body;
+}
+
+/**
+ * Performs the operation "extract a `Content-Type` value from |object|" as
+ * specified in the specification:
+ * https://fetch.spec.whatwg.org/#concept-bodyinit-extract
+ *
+ * This function assumes that instance.body is present.
+ *
+ * @param   Mixed  instance  Any options.body input
+ */
+function extractContentType(body) {
+	if (body === null) {
+		// body is null
+		return null;
+	} else if (typeof body === 'string') {
+		// body is string
+		return 'text/plain;charset=UTF-8';
+	} else if (isURLSearchParams(body)) {
+		// body is a URLSearchParams
+		return 'application/x-www-form-urlencoded;charset=UTF-8';
+	} else if (isBlob(body)) {
+		// body is blob
+		return body.type || null;
+	} else if (Buffer.isBuffer(body)) {
+		// body is buffer
+		return null;
+	} else if (Object.prototype.toString.call(body) === '[object ArrayBuffer]') {
+		// body is ArrayBuffer
+		return null;
+	} else if (ArrayBuffer.isView(body)) {
+		// body is ArrayBufferView
+		return null;
+	} else if (typeof body.getBoundary === 'function') {
+		// detect form data input from form-data module
+		return `multipart/form-data;boundary=${body.getBoundary()}`;
+	} else if (body instanceof Stream) {
+		// body is stream
+		// can't really do much about this
+		return null;
+	} else {
+		// Body constructor defaults other things to string
+		return 'text/plain;charset=UTF-8';
+	}
+}
+
+/**
+ * The Fetch Standard treats this as if "total bytes" is a property on the body.
+ * For us, we have to explicitly get it with a function.
+ *
+ * ref: https://fetch.spec.whatwg.org/#concept-body-total-bytes
+ *
+ * @param   Body    instance   Instance of Body
+ * @return  Number?            Number of bytes, or null if not possible
+ */
+function getTotalBytes(instance) {
+	const body = instance.body;
+
+
+	if (body === null) {
+		// body is null
+		return 0;
+	} else if (isBlob(body)) {
+		return body.size;
+	} else if (Buffer.isBuffer(body)) {
+		// body is buffer
+		return body.length;
+	} else if (body && typeof body.getLengthSync === 'function') {
+		// detect form data input from form-data module
+		if (body._lengthRetrievers && body._lengthRetrievers.length == 0 || // 1.x
+		body.hasKnownLength && body.hasKnownLength()) {
+			// 2.x
+			return body.getLengthSync();
+		}
+		return null;
+	} else {
+		// body is stream
+		return null;
+	}
+}
+
+/**
+ * Write a Body to a Node.js WritableStream (e.g. http.Request) object.
+ *
+ * @param   Body    instance   Instance of Body
+ * @return  Void
+ */
+function writeToStream(dest, instance) {
+	const body = instance.body;
+
+
+	if (body === null) {
+		// body is null
+		dest.end();
+	} else if (isBlob(body)) {
+		body.stream().pipe(dest);
+	} else if (Buffer.isBuffer(body)) {
+		// body is buffer
+		dest.write(body);
+		dest.end();
+	} else {
+		// body is stream
+		body.pipe(dest);
+	}
+}
+
+// expose Promise
+Body.Promise = global.Promise;
+
+/**
+ * headers.js
+ *
+ * Headers class offers convenient helpers
+ */
+
+const invalidTokenRegex = /[^\^_`a-zA-Z\-0-9!#$%&'*+.|~]/;
+const invalidHeaderCharRegex = /[^\t\x20-\x7e\x80-\xff]/;
+
+function validateName(name) {
+	name = `${name}`;
+	if (invalidTokenRegex.test(name) || name === '') {
+		throw new TypeError(`${name} is not a legal HTTP header name`);
+	}
+}
+
+function validateValue(value) {
+	value = `${value}`;
+	if (invalidHeaderCharRegex.test(value)) {
+		throw new TypeError(`${value} is not a legal HTTP header value`);
+	}
+}
+
+/**
+ * Find the key in the map object given a header name.
+ *
+ * Returns undefined if not found.
+ *
+ * @param   String  name  Header name
+ * @return  String|Undefined
+ */
+function find(map, name) {
+	name = name.toLowerCase();
+	for (const key in map) {
+		if (key.toLowerCase() === name) {
+			return key;
+		}
+	}
+	return undefined;
+}
+
+const MAP = Symbol('map');
+class Headers {
+	/**
+  * Headers class
+  *
+  * @param   Object  headers  Response headers
+  * @return  Void
+  */
+	constructor() {
+		let init = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : undefined;
+
+		this[MAP] = Object.create(null);
+
+		if (init instanceof Headers) {
+			const rawHeaders = init.raw();
+			const headerNames = Object.keys(rawHeaders);
+
+			for (const headerName of headerNames) {
+				for (const value of rawHeaders[headerName]) {
+					this.append(headerName, value);
+				}
+			}
+
+			return;
+		}
+
+		// We don't worry about converting prop to ByteString here as append()
+		// will handle it.
+		if (init == null) ; else if (typeof init === 'object') {
+			const method = init[Symbol.iterator];
+			if (method != null) {
+				if (typeof method !== 'function') {
+					throw new TypeError('Header pairs must be iterable');
+				}
+
+				// sequence<sequence<ByteString>>
+				// Note: per spec we have to first exhaust the lists then process them
+				const pairs = [];
+				for (const pair of init) {
+					if (typeof pair !== 'object' || typeof pair[Symbol.iterator] !== 'function') {
+						throw new TypeError('Each header pair must be iterable');
+					}
+					pairs.push(Array.from(pair));
+				}
+
+				for (const pair of pairs) {
+					if (pair.length !== 2) {
+						throw new TypeError('Each header pair must be a name/value tuple');
+					}
+					this.append(pair[0], pair[1]);
+				}
+			} else {
+				// record<ByteString, ByteString>
+				for (const key of Object.keys(init)) {
+					const value = init[key];
+					this.append(key, value);
+				}
+			}
+		} else {
+			throw new TypeError('Provided initializer must be an object');
+		}
+	}
+
+	/**
+  * Return combined header value given name
+  *
+  * @param   String  name  Header name
+  * @return  Mixed
+  */
+	get(name) {
+		name = `${name}`;
+		validateName(name);
+		const key = find(this[MAP], name);
+		if (key === undefined) {
+			return null;
+		}
+
+		return this[MAP][key].join(', ');
+	}
+
+	/**
+  * Iterate over all headers
+  *
+  * @param   Function  callback  Executed for each item with parameters (value, name, thisArg)
+  * @param   Boolean   thisArg   `this` context for callback function
+  * @return  Void
+  */
+	forEach(callback) {
+		let thisArg = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : undefined;
+
+		let pairs = getHeaders(this);
+		let i = 0;
+		while (i < pairs.length) {
+			var _pairs$i = pairs[i];
+			const name = _pairs$i[0],
+			      value = _pairs$i[1];
+
+			callback.call(thisArg, value, name, this);
+			pairs = getHeaders(this);
+			i++;
+		}
+	}
+
+	/**
+  * Overwrite header values given name
+  *
+  * @param   String  name   Header name
+  * @param   String  value  Header value
+  * @return  Void
+  */
+	set(name, value) {
+		name = `${name}`;
+		value = `${value}`;
+		validateName(name);
+		validateValue(value);
+		const key = find(this[MAP], name);
+		this[MAP][key !== undefined ? key : name] = [value];
+	}
+
+	/**
+  * Append a value onto existing header
+  *
+  * @param   String  name   Header name
+  * @param   String  value  Header value
+  * @return  Void
+  */
+	append(name, value) {
+		name = `${name}`;
+		value = `${value}`;
+		validateName(name);
+		validateValue(value);
+		const key = find(this[MAP], name);
+		if (key !== undefined) {
+			this[MAP][key].push(value);
+		} else {
+			this[MAP][name] = [value];
+		}
+	}
+
+	/**
+  * Check for header name existence
+  *
+  * @param   String   name  Header name
+  * @return  Boolean
+  */
+	has(name) {
+		name = `${name}`;
+		validateName(name);
+		return find(this[MAP], name) !== undefined;
+	}
+
+	/**
+  * Delete all header values given name
+  *
+  * @param   String  name  Header name
+  * @return  Void
+  */
+	delete(name) {
+		name = `${name}`;
+		validateName(name);
+		const key = find(this[MAP], name);
+		if (key !== undefined) {
+			delete this[MAP][key];
+		}
+	}
+
+	/**
+  * Return raw headers (non-spec api)
+  *
+  * @return  Object
+  */
+	raw() {
+		return this[MAP];
+	}
+
+	/**
+  * Get an iterator on keys.
+  *
+  * @return  Iterator
+  */
+	keys() {
+		return createHeadersIterator(this, 'key');
+	}
+
+	/**
+  * Get an iterator on values.
+  *
+  * @return  Iterator
+  */
+	values() {
+		return createHeadersIterator(this, 'value');
+	}
+
+	/**
+  * Get an iterator on entries.
+  *
+  * This is the default iterator of the Headers object.
+  *
+  * @return  Iterator
+  */
+	[Symbol.iterator]() {
+		return createHeadersIterator(this, 'key+value');
+	}
+}
+Headers.prototype.entries = Headers.prototype[Symbol.iterator];
+
+Object.defineProperty(Headers.prototype, Symbol.toStringTag, {
+	value: 'Headers',
+	writable: false,
+	enumerable: false,
+	configurable: true
+});
+
+Object.defineProperties(Headers.prototype, {
+	get: { enumerable: true },
+	forEach: { enumerable: true },
+	set: { enumerable: true },
+	append: { enumerable: true },
+	has: { enumerable: true },
+	delete: { enumerable: true },
+	keys: { enumerable: true },
+	values: { enumerable: true },
+	entries: { enumerable: true }
+});
+
+function getHeaders(headers) {
+	let kind = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'key+value';
+
+	const keys = Object.keys(headers[MAP]).sort();
+	return keys.map(kind === 'key' ? function (k) {
+		return k.toLowerCase();
+	} : kind === 'value' ? function (k) {
+		return headers[MAP][k].join(', ');
+	} : function (k) {
+		return [k.toLowerCase(), headers[MAP][k].join(', ')];
+	});
+}
+
+const INTERNAL = Symbol('internal');
+
+function createHeadersIterator(target, kind) {
+	const iterator = Object.create(HeadersIteratorPrototype);
+	iterator[INTERNAL] = {
+		target,
+		kind,
+		index: 0
+	};
+	return iterator;
+}
+
+const HeadersIteratorPrototype = Object.setPrototypeOf({
+	next() {
+		// istanbul ignore if
+		if (!this || Object.getPrototypeOf(this) !== HeadersIteratorPrototype) {
+			throw new TypeError('Value of `this` is not a HeadersIterator');
+		}
+
+		var _INTERNAL = this[INTERNAL];
+		const target = _INTERNAL.target,
+		      kind = _INTERNAL.kind,
+		      index = _INTERNAL.index;
+
+		const values = getHeaders(target, kind);
+		const len = values.length;
+		if (index >= len) {
+			return {
+				value: undefined,
+				done: true
+			};
+		}
+
+		this[INTERNAL].index = index + 1;
+
+		return {
+			value: values[index],
+			done: false
+		};
+	}
+}, Object.getPrototypeOf(Object.getPrototypeOf([][Symbol.iterator]())));
+
+Object.defineProperty(HeadersIteratorPrototype, Symbol.toStringTag, {
+	value: 'HeadersIterator',
+	writable: false,
+	enumerable: false,
+	configurable: true
+});
+
+/**
+ * Export the Headers object in a form that Node.js can consume.
+ *
+ * @param   Headers  headers
+ * @return  Object
+ */
+function exportNodeCompatibleHeaders(headers) {
+	const obj = Object.assign({ __proto__: null }, headers[MAP]);
+
+	// http.request() only supports string as Host header. This hack makes
+	// specifying custom Host header possible.
+	const hostHeaderKey = find(headers[MAP], 'Host');
+	if (hostHeaderKey !== undefined) {
+		obj[hostHeaderKey] = obj[hostHeaderKey][0];
+	}
+
+	return obj;
+}
+
+/**
+ * Create a Headers object from an object of headers, ignoring those that do
+ * not conform to HTTP grammar productions.
+ *
+ * @param   Object  obj  Object of headers
+ * @return  Headers
+ */
+function createHeadersLenient(obj) {
+	const headers = new Headers();
+	for (const name of Object.keys(obj)) {
+		if (invalidTokenRegex.test(name)) {
+			continue;
+		}
+		if (Array.isArray(obj[name])) {
+			for (const val of obj[name]) {
+				if (invalidHeaderCharRegex.test(val)) {
+					continue;
+				}
+				if (headers[MAP][name] === undefined) {
+					headers[MAP][name] = [val];
+				} else {
+					headers[MAP][name].push(val);
+				}
+			}
+		} else if (!invalidHeaderCharRegex.test(obj[name])) {
+			headers[MAP][name] = [obj[name]];
+		}
+	}
+	return headers;
+}
+
+const INTERNALS$1 = Symbol('Response internals');
+
+// fix an issue where "STATUS_CODES" aren't a named export for node <10
+const STATUS_CODES = http.STATUS_CODES;
+
+/**
+ * Response class
+ *
+ * @param   Stream  body  Readable stream
+ * @param   Object  opts  Response options
+ * @return  Void
+ */
+class Response {
+	constructor() {
+		let body = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : null;
+		let opts = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+		Body.call(this, body, opts);
+
+		const status = opts.status || 200;
+		const headers = new Headers(opts.headers);
+
+		if (body != null && !headers.has('Content-Type')) {
+			const contentType = extractContentType(body);
+			if (contentType) {
+				headers.append('Content-Type', contentType);
+			}
+		}
+
+		this[INTERNALS$1] = {
+			url: opts.url,
+			status,
+			statusText: opts.statusText || STATUS_CODES[status],
+			headers,
+			counter: opts.counter
+		};
+	}
+
+	get url() {
+		return this[INTERNALS$1].url || '';
+	}
+
+	get status() {
+		return this[INTERNALS$1].status;
+	}
+
+	/**
+  * Convenience property representing if the request ended normally
+  */
+	get ok() {
+		return this[INTERNALS$1].status >= 200 && this[INTERNALS$1].status < 300;
+	}
+
+	get redirected() {
+		return this[INTERNALS$1].counter > 0;
+	}
+
+	get statusText() {
+		return this[INTERNALS$1].statusText;
+	}
+
+	get headers() {
+		return this[INTERNALS$1].headers;
+	}
+
+	/**
+  * Clone this response
+  *
+  * @return  Response
+  */
+	clone() {
+		return new Response(clone(this), {
+			url: this.url,
+			status: this.status,
+			statusText: this.statusText,
+			headers: this.headers,
+			ok: this.ok,
+			redirected: this.redirected
+		});
+	}
+}
+
+Body.mixIn(Response.prototype);
+
+Object.defineProperties(Response.prototype, {
+	url: { enumerable: true },
+	status: { enumerable: true },
+	ok: { enumerable: true },
+	redirected: { enumerable: true },
+	statusText: { enumerable: true },
+	headers: { enumerable: true },
+	clone: { enumerable: true }
+});
+
+Object.defineProperty(Response.prototype, Symbol.toStringTag, {
+	value: 'Response',
+	writable: false,
+	enumerable: false,
+	configurable: true
+});
+
+const INTERNALS$2 = Symbol('Request internals');
+
+// fix an issue where "format", "parse" aren't a named export for node <10
+const parse_url = Url.parse;
+const format_url = Url.format;
+
+const streamDestructionSupported = 'destroy' in Stream.Readable.prototype;
+
+/**
+ * Check if a value is an instance of Request.
+ *
+ * @param   Mixed   input
+ * @return  Boolean
+ */
+function isRequest(input) {
+	return typeof input === 'object' && typeof input[INTERNALS$2] === 'object';
+}
+
+function isAbortSignal(signal) {
+	const proto = signal && typeof signal === 'object' && Object.getPrototypeOf(signal);
+	return !!(proto && proto.constructor.name === 'AbortSignal');
+}
+
+/**
+ * Request class
+ *
+ * @param   Mixed   input  Url or Request instance
+ * @param   Object  init   Custom options
+ * @return  Void
+ */
+class Request {
+	constructor(input) {
+		let init = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {};
+
+		let parsedURL;
+
+		// normalize input
+		if (!isRequest(input)) {
+			if (input && input.href) {
+				// in order to support Node.js' Url objects; though WHATWG's URL objects
+				// will fall into this branch also (since their `toString()` will return
+				// `href` property anyway)
+				parsedURL = parse_url(input.href);
+			} else {
+				// coerce input to a string before attempting to parse
+				parsedURL = parse_url(`${input}`);
+			}
+			input = {};
+		} else {
+			parsedURL = parse_url(input.url);
+		}
+
+		let method = init.method || input.method || 'GET';
+		method = method.toUpperCase();
+
+		if ((init.body != null || isRequest(input) && input.body !== null) && (method === 'GET' || method === 'HEAD')) {
+			throw new TypeError('Request with GET/HEAD method cannot have body');
+		}
+
+		let inputBody = init.body != null ? init.body : isRequest(input) && input.body !== null ? clone(input) : null;
+
+		Body.call(this, inputBody, {
+			timeout: init.timeout || input.timeout || 0,
+			size: init.size || input.size || 0
+		});
+
+		const headers = new Headers(init.headers || input.headers || {});
+
+		if (inputBody != null && !headers.has('Content-Type')) {
+			const contentType = extractContentType(inputBody);
+			if (contentType) {
+				headers.append('Content-Type', contentType);
+			}
+		}
+
+		let signal = isRequest(input) ? input.signal : null;
+		if ('signal' in init) signal = init.signal;
+
+		if (signal != null && !isAbortSignal(signal)) {
+			throw new TypeError('Expected signal to be an instanceof AbortSignal');
+		}
+
+		this[INTERNALS$2] = {
+			method,
+			redirect: init.redirect || input.redirect || 'follow',
+			headers,
+			parsedURL,
+			signal
+		};
+
+		// node-fetch-only options
+		this.follow = init.follow !== undefined ? init.follow : input.follow !== undefined ? input.follow : 20;
+		this.compress = init.compress !== undefined ? init.compress : input.compress !== undefined ? input.compress : true;
+		this.counter = init.counter || input.counter || 0;
+		this.agent = init.agent || input.agent;
+	}
+
+	get method() {
+		return this[INTERNALS$2].method;
+	}
+
+	get url() {
+		return format_url(this[INTERNALS$2].parsedURL);
+	}
+
+	get headers() {
+		return this[INTERNALS$2].headers;
+	}
+
+	get redirect() {
+		return this[INTERNALS$2].redirect;
+	}
+
+	get signal() {
+		return this[INTERNALS$2].signal;
+	}
+
+	/**
+  * Clone this request
+  *
+  * @return  Request
+  */
+	clone() {
+		return new Request(this);
+	}
+}
+
+Body.mixIn(Request.prototype);
+
+Object.defineProperty(Request.prototype, Symbol.toStringTag, {
+	value: 'Request',
+	writable: false,
+	enumerable: false,
+	configurable: true
+});
+
+Object.defineProperties(Request.prototype, {
+	method: { enumerable: true },
+	url: { enumerable: true },
+	headers: { enumerable: true },
+	redirect: { enumerable: true },
+	clone: { enumerable: true },
+	signal: { enumerable: true }
+});
+
+/**
+ * Convert a Request to Node.js http request options.
+ *
+ * @param   Request  A Request instance
+ * @return  Object   The options object to be passed to http.request
+ */
+function getNodeRequestOptions(request) {
+	const parsedURL = request[INTERNALS$2].parsedURL;
+	const headers = new Headers(request[INTERNALS$2].headers);
+
+	// fetch step 1.3
+	if (!headers.has('Accept')) {
+		headers.set('Accept', '*/*');
+	}
+
+	// Basic fetch
+	if (!parsedURL.protocol || !parsedURL.hostname) {
+		throw new TypeError('Only absolute URLs are supported');
+	}
+
+	if (!/^https?:$/.test(parsedURL.protocol)) {
+		throw new TypeError('Only HTTP(S) protocols are supported');
+	}
+
+	if (request.signal && request.body instanceof Stream.Readable && !streamDestructionSupported) {
+		throw new Error('Cancellation of streamed requests with AbortSignal is not supported in node < 8');
+	}
+
+	// HTTP-network-or-cache fetch steps 2.4-2.7
+	let contentLengthValue = null;
+	if (request.body == null && /^(POST|PUT)$/i.test(request.method)) {
+		contentLengthValue = '0';
+	}
+	if (request.body != null) {
+		const totalBytes = getTotalBytes(request);
+		if (typeof totalBytes === 'number') {
+			contentLengthValue = String(totalBytes);
+		}
+	}
+	if (contentLengthValue) {
+		headers.set('Content-Length', contentLengthValue);
+	}
+
+	// HTTP-network-or-cache fetch step 2.11
+	if (!headers.has('User-Agent')) {
+		headers.set('User-Agent', 'node-fetch/1.0 (+https://github.com/bitinn/node-fetch)');
+	}
+
+	// HTTP-network-or-cache fetch step 2.15
+	if (request.compress && !headers.has('Accept-Encoding')) {
+		headers.set('Accept-Encoding', 'gzip,deflate');
+	}
+
+	let agent = request.agent;
+	if (typeof agent === 'function') {
+		agent = agent(parsedURL);
+	}
+
+	if (!headers.has('Connection') && !agent) {
+		headers.set('Connection', 'close');
+	}
+
+	// HTTP-network fetch step 4.2
+	// chunked encoding is handled by Node.js
+
+	return Object.assign({}, parsedURL, {
+		method: request.method,
+		headers: exportNodeCompatibleHeaders(headers),
+		agent
+	});
+}
+
+/**
+ * abort-error.js
+ *
+ * AbortError interface for cancelled requests
+ */
+
+/**
+ * Create AbortError instance
+ *
+ * @param   String      message      Error message for human
+ * @return  AbortError
+ */
+function AbortError(message) {
+  Error.call(this, message);
+
+  this.type = 'aborted';
+  this.message = message;
+
+  // hide custom error implementation details from end-users
+  Error.captureStackTrace(this, this.constructor);
+}
+
+AbortError.prototype = Object.create(Error.prototype);
+AbortError.prototype.constructor = AbortError;
+AbortError.prototype.name = 'AbortError';
+
+// fix an issue where "PassThrough", "resolve" aren't a named export for node <10
+const PassThrough$1 = Stream.PassThrough;
+const resolve_url = Url.resolve;
+
+/**
+ * Fetch function
+ *
+ * @param   Mixed    url   Absolute url or Request instance
+ * @param   Object   opts  Fetch options
+ * @return  Promise
+ */
+function fetch(url, opts) {
+
+	// allow custom promise
+	if (!fetch.Promise) {
+		throw new Error('native promise missing, set fetch.Promise to your favorite alternative');
+	}
+
+	Body.Promise = fetch.Promise;
+
+	// wrap http.request into fetch
+	return new fetch.Promise(function (resolve, reject) {
+		// build request object
+		const request = new Request(url, opts);
+		const options = getNodeRequestOptions(request);
+
+		const send = (options.protocol === 'https:' ? https : http).request;
+		const signal = request.signal;
+
+		let response = null;
+
+		const abort = function abort() {
+			let error = new AbortError('The user aborted a request.');
+			reject(error);
+			if (request.body && request.body instanceof Stream.Readable) {
+				request.body.destroy(error);
+			}
+			if (!response || !response.body) return;
+			response.body.emit('error', error);
+		};
+
+		if (signal && signal.aborted) {
+			abort();
+			return;
+		}
+
+		const abortAndFinalize = function abortAndFinalize() {
+			abort();
+			finalize();
+		};
+
+		// send request
+		const req = send(options);
+		let reqTimeout;
+
+		if (signal) {
+			signal.addEventListener('abort', abortAndFinalize);
+		}
+
+		function finalize() {
+			req.abort();
+			if (signal) signal.removeEventListener('abort', abortAndFinalize);
+			clearTimeout(reqTimeout);
+		}
+
+		if (request.timeout) {
+			req.once('socket', function (socket) {
+				reqTimeout = setTimeout(function () {
+					reject(new FetchError(`network timeout at: ${request.url}`, 'request-timeout'));
+					finalize();
+				}, request.timeout);
+			});
+		}
+
+		req.on('error', function (err) {
+			reject(new FetchError(`request to ${request.url} failed, reason: ${err.message}`, 'system', err));
+			finalize();
+		});
+
+		req.on('response', function (res) {
+			clearTimeout(reqTimeout);
+
+			const headers = createHeadersLenient(res.headers);
+
+			// HTTP fetch step 5
+			if (fetch.isRedirect(res.statusCode)) {
+				// HTTP fetch step 5.2
+				const location = headers.get('Location');
+
+				// HTTP fetch step 5.3
+				const locationURL = location === null ? null : resolve_url(request.url, location);
+
+				// HTTP fetch step 5.5
+				switch (request.redirect) {
+					case 'error':
+						reject(new FetchError(`redirect mode is set to error: ${request.url}`, 'no-redirect'));
+						finalize();
+						return;
+					case 'manual':
+						// node-fetch-specific step: make manual redirect a bit easier to use by setting the Location header value to the resolved URL.
+						if (locationURL !== null) {
+							// handle corrupted header
+							try {
+								headers.set('Location', locationURL);
+							} catch (err) {
+								// istanbul ignore next: nodejs server prevent invalid response headers, we can't test this through normal request
+								reject(err);
+							}
+						}
+						break;
+					case 'follow':
+						// HTTP-redirect fetch step 2
+						if (locationURL === null) {
+							break;
+						}
+
+						// HTTP-redirect fetch step 5
+						if (request.counter >= request.follow) {
+							reject(new FetchError(`maximum redirect reached at: ${request.url}`, 'max-redirect'));
+							finalize();
+							return;
+						}
+
+						// HTTP-redirect fetch step 6 (counter increment)
+						// Create a new Request object.
+						const requestOpts = {
+							headers: new Headers(request.headers),
+							follow: request.follow,
+							counter: request.counter + 1,
+							agent: request.agent,
+							compress: request.compress,
+							method: request.method,
+							body: request.body,
+							signal: request.signal,
+							timeout: request.timeout
+						};
+
+						// HTTP-redirect fetch step 9
+						if (res.statusCode !== 303 && request.body && getTotalBytes(request) === null) {
+							reject(new FetchError('Cannot follow redirect with body being a readable stream', 'unsupported-redirect'));
+							finalize();
+							return;
+						}
+
+						// HTTP-redirect fetch step 11
+						if (res.statusCode === 303 || (res.statusCode === 301 || res.statusCode === 302) && request.method === 'POST') {
+							requestOpts.method = 'GET';
+							requestOpts.body = undefined;
+							requestOpts.headers.delete('content-length');
+						}
+
+						// HTTP-redirect fetch step 15
+						resolve(fetch(new Request(locationURL, requestOpts)));
+						finalize();
+						return;
+				}
+			}
+
+			// prepare response
+			res.once('end', function () {
+				if (signal) signal.removeEventListener('abort', abortAndFinalize);
+			});
+			let body = res.pipe(new PassThrough$1());
+
+			const response_options = {
+				url: request.url,
+				status: res.statusCode,
+				statusText: res.statusMessage,
+				headers: headers,
+				size: request.size,
+				timeout: request.timeout,
+				counter: request.counter
+			};
+
+			// HTTP-network fetch step 12.1.1.3
+			const codings = headers.get('Content-Encoding');
+
+			// HTTP-network fetch step 12.1.1.4: handle content codings
+
+			// in following scenarios we ignore compression support
+			// 1. compression support is disabled
+			// 2. HEAD request
+			// 3. no Content-Encoding header
+			// 4. no content response (204)
+			// 5. content not modified response (304)
+			if (!request.compress || request.method === 'HEAD' || codings === null || res.statusCode === 204 || res.statusCode === 304) {
+				response = new Response(body, response_options);
+				resolve(response);
+				return;
+			}
+
+			// For Node v6+
+			// Be less strict when decoding compressed responses, since sometimes
+			// servers send slightly invalid responses that are still accepted
+			// by common browsers.
+			// Always using Z_SYNC_FLUSH is what cURL does.
+			const zlibOptions = {
+				flush: zlib.Z_SYNC_FLUSH,
+				finishFlush: zlib.Z_SYNC_FLUSH
+			};
+
+			// for gzip
+			if (codings == 'gzip' || codings == 'x-gzip') {
+				body = body.pipe(zlib.createGunzip(zlibOptions));
+				response = new Response(body, response_options);
+				resolve(response);
+				return;
+			}
+
+			// for deflate
+			if (codings == 'deflate' || codings == 'x-deflate') {
+				// handle the infamous raw deflate response from old servers
+				// a hack for old IIS and Apache servers
+				const raw = res.pipe(new PassThrough$1());
+				raw.once('data', function (chunk) {
+					// see http://stackoverflow.com/questions/37519828
+					if ((chunk[0] & 0x0F) === 0x08) {
+						body = body.pipe(zlib.createInflate());
+					} else {
+						body = body.pipe(zlib.createInflateRaw());
+					}
+					response = new Response(body, response_options);
+					resolve(response);
+				});
+				return;
+			}
+
+			// for br
+			if (codings == 'br' && typeof zlib.createBrotliDecompress === 'function') {
+				body = body.pipe(zlib.createBrotliDecompress());
+				response = new Response(body, response_options);
+				resolve(response);
+				return;
+			}
+
+			// otherwise, use response as-is
+			response = new Response(body, response_options);
+			resolve(response);
+		});
+
+		writeToStream(req, request);
+	});
+}
+/**
+ * Redirect code matching
+ *
+ * @param   Number   code  Status code
+ * @return  Boolean
+ */
+fetch.isRedirect = function (code) {
+	return code === 301 || code === 302 || code === 303 || code === 307 || code === 308;
+};
+
+// expose Promise
+fetch.Promise = global.Promise;
+
+module.exports = exports = fetch;
+Object.defineProperty(exports, "__esModule", { value: true });
+exports.default = exports;
+exports.Headers = Headers;
+exports.Request = Request;
+exports.Response = Response;
+exports.FetchError = FetchError;
+
+
+/***/ }),
+
+/***/ 370:
+/***/ (function(module) {
+
+module.exports = deprecate
+
+const loggedMessages = {}
+
+function deprecate (message) {
+  if (loggedMessages[message]) {
+    return
+  }
+
+  console.warn(`DEPRECATED (@octokit/rest): ${message}`)
+  loggedMessages[message] = 1
+}
+
+
+/***/ }),
+
+/***/ 385:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var isPlainObject = _interopDefault(__webpack_require__(626));
+var universalUserAgent = __webpack_require__(796);
+
+function lowercaseKeys(object) {
+  if (!object) {
+    return {};
+  }
+
+  return Object.keys(object).reduce((newObj, key) => {
+    newObj[key.toLowerCase()] = object[key];
+    return newObj;
+  }, {});
+}
+
+function mergeDeep(defaults, options) {
+  const result = Object.assign({}, defaults);
+  Object.keys(options).forEach(key => {
+    if (isPlainObject(options[key])) {
+      if (!(key in defaults)) Object.assign(result, {
+        [key]: options[key]
+      });else result[key] = mergeDeep(defaults[key], options[key]);
+    } else {
+      Object.assign(result, {
+        [key]: options[key]
+      });
+    }
+  });
+  return result;
+}
+
+function merge(defaults, route, options) {
+  if (typeof route === "string") {
+    let [method, url] = route.split(" ");
+    options = Object.assign(url ? {
+      method,
+      url
+    } : {
+      url: method
+    }, options);
+  } else {
+    options = Object.assign({}, route);
+  } // lowercase header names before merging with defaults to avoid duplicates
+
+
+  options.headers = lowercaseKeys(options.headers);
+  const mergedOptions = mergeDeep(defaults || {}, options); // mediaType.previews arrays are merged, instead of overwritten
+
+  if (defaults && defaults.mediaType.previews.length) {
+    mergedOptions.mediaType.previews = defaults.mediaType.previews.filter(preview => !mergedOptions.mediaType.previews.includes(preview)).concat(mergedOptions.mediaType.previews);
+  }
+
+  mergedOptions.mediaType.previews = mergedOptions.mediaType.previews.map(preview => preview.replace(/-preview/, ""));
+  return mergedOptions;
+}
+
+function addQueryParameters(url, parameters) {
+  const separator = /\?/.test(url) ? "&" : "?";
+  const names = Object.keys(parameters);
+
+  if (names.length === 0) {
+    return url;
+  }
+
+  return url + separator + names.map(name => {
+    if (name === "q") {
+      return "q=" + parameters.q.split("+").map(encodeURIComponent).join("+");
+    }
+
+    return `${name}=${encodeURIComponent(parameters[name])}`;
+  }).join("&");
+}
+
+const urlVariableRegex = /\{[^}]+\}/g;
+
+function removeNonChars(variableName) {
+  return variableName.replace(/^\W+|\W+$/g, "").split(/,/);
+}
+
+function extractUrlVariableNames(url) {
+  const matches = url.match(urlVariableRegex);
+
+  if (!matches) {
+    return [];
+  }
+
+  return matches.map(removeNonChars).reduce((a, b) => a.concat(b), []);
+}
+
+function omit(object, keysToOmit) {
+  return Object.keys(object).filter(option => !keysToOmit.includes(option)).reduce((obj, key) => {
+    obj[key] = object[key];
+    return obj;
+  }, {});
+}
+
+// Based on https://github.com/bramstein/url-template, licensed under BSD
+// TODO: create separate package.
+//
+// Copyright (c) 2012-2014, Bram Stein
+// All rights reserved.
+// Redistribution and use in source and binary forms, with or without
+// modification, are permitted provided that the following conditions
+// are met:
+//  1. Redistributions of source code must retain the above copyright
+//     notice, this list of conditions and the following disclaimer.
+//  2. Redistributions in binary form must reproduce the above copyright
+//     notice, this list of conditions and the following disclaimer in the
+//     documentation and/or other materials provided with the distribution.
+//  3. The name of the author may not be used to endorse or promote products
+//     derived from this software without specific prior written permission.
+// THIS SOFTWARE IS PROVIDED BY THE AUTHOR "AS IS" AND ANY EXPRESS OR IMPLIED
+// WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+// MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
+// EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT,
+// INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING,
+// BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
+// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY
+// OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
+// NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE,
+// EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+/* istanbul ignore file */
+function encodeReserved(str) {
+  return str.split(/(%[0-9A-Fa-f]{2})/g).map(function (part) {
+    if (!/%[0-9A-Fa-f]/.test(part)) {
+      part = encodeURI(part).replace(/%5B/g, "[").replace(/%5D/g, "]");
+    }
+
+    return part;
+  }).join("");
+}
+
+function encodeUnreserved(str) {
+  return encodeURIComponent(str).replace(/[!'()*]/g, function (c) {
+    return "%" + c.charCodeAt(0).toString(16).toUpperCase();
+  });
+}
+
+function encodeValue(operator, value, key) {
+  value = operator === "+" || operator === "#" ? encodeReserved(value) : encodeUnreserved(value);
+
+  if (key) {
+    return encodeUnreserved(key) + "=" + value;
+  } else {
+    return value;
+  }
+}
+
+function isDefined(value) {
+  return value !== undefined && value !== null;
+}
+
+function isKeyOperator(operator) {
+  return operator === ";" || operator === "&" || operator === "?";
+}
+
+function getValues(context, operator, key, modifier) {
+  var value = context[key],
+      result = [];
+
+  if (isDefined(value) && value !== "") {
+    if (typeof value === "string" || typeof value === "number" || typeof value === "boolean") {
+      value = value.toString();
+
+      if (modifier && modifier !== "*") {
+        value = value.substring(0, parseInt(modifier, 10));
+      }
+
+      result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
+    } else {
+      if (modifier === "*") {
+        if (Array.isArray(value)) {
+          value.filter(isDefined).forEach(function (value) {
+            result.push(encodeValue(operator, value, isKeyOperator(operator) ? key : ""));
+          });
+        } else {
+          Object.keys(value).forEach(function (k) {
+            if (isDefined(value[k])) {
+              result.push(encodeValue(operator, value[k], k));
+            }
+          });
+        }
+      } else {
+        const tmp = [];
+
+        if (Array.isArray(value)) {
+          value.filter(isDefined).forEach(function (value) {
+            tmp.push(encodeValue(operator, value));
+          });
+        } else {
+          Object.keys(value).forEach(function (k) {
+            if (isDefined(value[k])) {
+              tmp.push(encodeUnreserved(k));
+              tmp.push(encodeValue(operator, value[k].toString()));
+            }
+          });
+        }
+
+        if (isKeyOperator(operator)) {
+          result.push(encodeUnreserved(key) + "=" + tmp.join(","));
+        } else if (tmp.length !== 0) {
+          result.push(tmp.join(","));
+        }
+      }
+    }
+  } else {
+    if (operator === ";") {
+      if (isDefined(value)) {
+        result.push(encodeUnreserved(key));
+      }
+    } else if (value === "" && (operator === "&" || operator === "?")) {
+      result.push(encodeUnreserved(key) + "=");
+    } else if (value === "") {
+      result.push("");
+    }
+  }
+
+  return result;
+}
+
+function parseUrl(template) {
+  return {
+    expand: expand.bind(null, template)
+  };
+}
+
+function expand(template, context) {
+  var operators = ["+", "#", ".", "/", ";", "?", "&"];
+  return template.replace(/\{([^\{\}]+)\}|([^\{\}]+)/g, function (_, expression, literal) {
+    if (expression) {
+      let operator = "";
+      const values = [];
+
+      if (operators.indexOf(expression.charAt(0)) !== -1) {
+        operator = expression.charAt(0);
+        expression = expression.substr(1);
+      }
+
+      expression.split(/,/g).forEach(function (variable) {
+        var tmp = /([^:\*]*)(?::(\d+)|(\*))?/.exec(variable);
+        values.push(getValues(context, operator, tmp[1], tmp[2] || tmp[3]));
+      });
+
+      if (operator && operator !== "+") {
+        var separator = ",";
+
+        if (operator === "?") {
+          separator = "&";
+        } else if (operator !== "#") {
+          separator = operator;
+        }
+
+        return (values.length !== 0 ? operator : "") + values.join(separator);
+      } else {
+        return values.join(",");
+      }
+    } else {
+      return encodeReserved(literal);
+    }
+  });
+}
+
+function parse(options) {
+  // https://fetch.spec.whatwg.org/#methods
+  let method = options.method.toUpperCase(); // replace :varname with {varname} to make it RFC 6570 compatible
+
+  let url = (options.url || "/").replace(/:([a-z]\w+)/g, "{+$1}");
+  let headers = Object.assign({}, options.headers);
+  let body;
+  let parameters = omit(options, ["method", "baseUrl", "url", "headers", "request", "mediaType"]); // extract variable names from URL to calculate remaining variables later
+
+  const urlVariableNames = extractUrlVariableNames(url);
+  url = parseUrl(url).expand(parameters);
+
+  if (!/^http/.test(url)) {
+    url = options.baseUrl + url;
+  }
+
+  const omittedParameters = Object.keys(options).filter(option => urlVariableNames.includes(option)).concat("baseUrl");
+  const remainingParameters = omit(parameters, omittedParameters);
+  const isBinaryRequset = /application\/octet-stream/i.test(headers.accept);
+
+  if (!isBinaryRequset) {
+    if (options.mediaType.format) {
+      // e.g. application/vnd.github.v3+json => application/vnd.github.v3.raw
+      headers.accept = headers.accept.split(/,/).map(preview => preview.replace(/application\/vnd(\.\w+)(\.v3)?(\.\w+)?(\+json)?$/, `application/vnd$1$2.${options.mediaType.format}`)).join(",");
+    }
+
+    if (options.mediaType.previews.length) {
+      const previewsFromAcceptHeader = headers.accept.match(/[\w-]+(?=-preview)/g) || [];
+      headers.accept = previewsFromAcceptHeader.concat(options.mediaType.previews).map(preview => {
+        const format = options.mediaType.format ? `.${options.mediaType.format}` : "+json";
+        return `application/vnd.github.${preview}-preview${format}`;
+      }).join(",");
+    }
+  } // for GET/HEAD requests, set URL query parameters from remaining parameters
+  // for PATCH/POST/PUT/DELETE requests, set request body from remaining parameters
+
+
+  if (["GET", "HEAD"].includes(method)) {
+    url = addQueryParameters(url, remainingParameters);
+  } else {
+    if ("data" in remainingParameters) {
+      body = remainingParameters.data;
+    } else {
+      if (Object.keys(remainingParameters).length) {
+        body = remainingParameters;
+      } else {
+        headers["content-length"] = 0;
+      }
+    }
+  } // default content-type for JSON if body is set
+
+
+  if (!headers["content-type"] && typeof body !== "undefined") {
+    headers["content-type"] = "application/json; charset=utf-8";
+  } // GitHub expects 'content-length: 0' header for PUT/PATCH requests without body.
+  // fetch does not allow to set `content-length` header, but we can set body to an empty string
+
+
+  if (["PATCH", "PUT"].includes(method) && typeof body === "undefined") {
+    body = "";
+  } // Only return body/request keys if present
+
+
+  return Object.assign({
+    method,
+    url,
+    headers
+  }, typeof body !== "undefined" ? {
+    body
+  } : null, options.request ? {
+    request: options.request
+  } : null);
+}
+
+function endpointWithDefaults(defaults, route, options) {
+  return parse(merge(defaults, route, options));
+}
+
+function withDefaults(oldDefaults, newDefaults) {
+  const DEFAULTS = merge(oldDefaults, newDefaults);
+  const endpoint = endpointWithDefaults.bind(null, DEFAULTS);
+  return Object.assign(endpoint, {
+    DEFAULTS,
+    defaults: withDefaults.bind(null, DEFAULTS),
+    merge: merge.bind(null, DEFAULTS),
+    parse
+  });
+}
+
+const VERSION = "5.5.2";
+
+const userAgent = `octokit-endpoint.js/${VERSION} ${universalUserAgent.getUserAgent()}`; // DEFAULTS has all properties set that EndpointOptions has, except url.
+// So we use RequestParameters and add method as additional required property.
+
+const DEFAULTS = {
+  method: "GET",
+  baseUrl: "https://api.github.com",
+  headers: {
+    accept: "application/vnd.github.v3+json",
+    "user-agent": userAgent
+  },
+  mediaType: {
+    format: "",
+    previews: []
+  }
+};
+
+const endpoint = withDefaults(null, DEFAULTS);
+
+exports.endpoint = endpoint;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 389:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+
+const fs = __webpack_require__(747);
+const shebangCommand = __webpack_require__(866);
+
+function readShebang(command) {
+    // Read the first 150 bytes from the file
+    const size = 150;
+    let buffer;
+
+    if (Buffer.alloc) {
+        // Node.js v4.5+ / v5.10+
+        buffer = Buffer.alloc(size);
+    } else {
+        // Old Node.js API
+        buffer = new Buffer(size);
+        buffer.fill(0); // zero-fill
+    }
+
+    let fd;
+
+    try {
+        fd = fs.openSync(command, 'r');
+        fs.readSync(fd, buffer, 0, size, 0);
+        fs.closeSync(fd);
+    } catch (e) { /* Empty */ }
+
+    // Attempt to extract shebang (null is returned if not a shebang)
+    return shebangCommand(buffer.toString());
+}
+
+module.exports = readShebang;
+
+
+/***/ }),
+
+/***/ 393:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const command_1 = __webpack_require__(431);
+const os = __importStar(__webpack_require__(87));
+const path = __importStar(__webpack_require__(622));
+/**
+ * The code to exit an action
+ */
+var ExitCode;
+(function (ExitCode) {
+    /**
+     * A code indicating that the action was successful
+     */
+    ExitCode[ExitCode["Success"] = 0] = "Success";
+    /**
+     * A code indicating that the action was a failure
+     */
+    ExitCode[ExitCode["Failure"] = 1] = "Failure";
+})(ExitCode = exports.ExitCode || (exports.ExitCode = {}));
+//-----------------------------------------------------------------------
+// Variables
+//-----------------------------------------------------------------------
+/**
+ * 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
+ */
+function exportVariable(name, val) {
+    process.env[name] = val;
+    command_1.issueCommand('set-env', { name }, val);
+}
+exports.exportVariable = exportVariable;
+/**
+ * Registers a secret which will get masked from logs
+ * @param secret value of the secret
+ */
+function setSecret(secret) {
+    command_1.issueCommand('add-mask', {}, secret);
+}
+exports.setSecret = setSecret;
+/**
+ * Prepends inputPath to the PATH (for this action and future actions)
+ * @param inputPath
+ */
+function addPath(inputPath) {
+    command_1.issueCommand('add-path', {}, inputPath);
+    process.env['PATH'] = `${inputPath}${path.delimiter}${process.env['PATH']}`;
+}
+exports.addPath = addPath;
+/**
+ * Gets the value of an input.  The value is also trimmed.
+ *
+ * @param     name     name of the input to get
+ * @param     options  optional. See InputOptions.
+ * @returns   string
+ */
+function getInput(name, options) {
+    const val = process.env[`INPUT_${name.replace(/ /g, '_').toUpperCase()}`] || '';
+    if (options && options.required && !val) {
+        throw new Error(`Input required and not supplied: ${name}`);
+    }
+    return val.trim();
+}
+exports.getInput = getInput;
+/**
+ * Sets the value of an output.
+ *
+ * @param     name     name of the output to set
+ * @param     value    value to store
+ */
+function setOutput(name, value) {
+    command_1.issueCommand('set-output', { name }, value);
+}
+exports.setOutput = setOutput;
+//-----------------------------------------------------------------------
+// Results
+//-----------------------------------------------------------------------
+/**
+ * Sets the action status to failed.
+ * When the action exits it will be with an exit code of 1
+ * @param message add error issue message
+ */
+function setFailed(message) {
+    process.exitCode = ExitCode.Failure;
+    error(message);
+}
+exports.setFailed = setFailed;
+//-----------------------------------------------------------------------
+// Logging Commands
+//-----------------------------------------------------------------------
+/**
+ * Writes debug message to user log
+ * @param message debug message
+ */
+function debug(message) {
+    command_1.issueCommand('debug', {}, message);
+}
+exports.debug = debug;
+/**
+ * Adds an error issue
+ * @param message error issue message
+ */
+function error(message) {
+    command_1.issue('error', message);
+}
+exports.error = error;
+/**
+ * Adds an warning issue
+ * @param message warning issue message
+ */
+function warning(message) {
+    command_1.issue('warning', message);
+}
+exports.warning = warning;
+/**
+ * Writes info to log with console.log.
+ * @param message info message
+ */
+function info(message) {
+    process.stdout.write(message + os.EOL);
+}
+exports.info = info;
+/**
+ * Begin an output group.
+ *
+ * Output until the next `groupEnd` will be foldable in this group
+ *
+ * @param name The name of the output group
+ */
+function startGroup(name) {
+    command_1.issue('group', name);
+}
+exports.startGroup = startGroup;
+/**
+ * End an output group.
+ */
+function endGroup() {
+    command_1.issue('endgroup');
+}
+exports.endGroup = endGroup;
+/**
+ * Wrap an asynchronous function call in a group.
+ *
+ * Returns the same type as the function itself.
+ *
+ * @param name The name of the group
+ * @param fn The function to wrap in the group
+ */
+function group(name, fn) {
+    return __awaiter(this, void 0, void 0, function* () {
+        startGroup(name);
+        let result;
+        try {
+            result = yield fn();
+        }
+        finally {
+            endGroup();
+        }
+        return result;
+    });
+}
+exports.group = group;
+//-----------------------------------------------------------------------
+// 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
+ */
+function saveState(name, value) {
+    command_1.issueCommand('save-state', { name }, value);
+}
+exports.saveState = saveState;
+/**
+ * Gets the value of an state set by this action's main execution.
+ *
+ * @param     name     name of the state to get
+ * @returns   string
+ */
+function getState(name) {
+    return process.env[`STATE_${name}`] || '';
+}
+exports.getState = getState;
+//# sourceMappingURL=core.js.map
+
+/***/ }),
+
+/***/ 402:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = Octokit;
+
+const { request } = __webpack_require__(753);
+const Hook = __webpack_require__(523);
+
+const parseClientOptions = __webpack_require__(294);
+
+function Octokit(plugins, options) {
+  options = options || {};
+  const hook = new Hook.Collection();
+  const log = Object.assign(
+    {
+      debug: () => {},
+      info: () => {},
+      warn: console.warn,
+      error: console.error
+    },
+    options && options.log
+  );
+  const api = {
+    hook,
+    log,
+    request: request.defaults(parseClientOptions(options, log, hook))
+  };
+
+  plugins.forEach(pluginFunction => pluginFunction(api, options));
+
+  return api;
+}
+
+
+/***/ }),
+
+/***/ 413:
+/***/ (function(module) {
+
+module.exports = require("stream");
+
+/***/ }),
+
+/***/ 427:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+// Older verions of Node.js might not have `util.getSystemErrorName()`.
+// In that case, fall back to a deprecated internal.
+const util = __webpack_require__(669);
+
+let uv;
+
+if (typeof util.getSystemErrorName === 'function') {
+	module.exports = util.getSystemErrorName;
+} else {
+	try {
+		uv = process.binding('uv');
+
+		if (typeof uv.errname !== 'function') {
+			throw new TypeError('uv.errname is not a function');
+		}
+	} catch (err) {
+		console.error('execa/lib/errname: unable to establish process.binding(\'uv\')', err);
+		uv = null;
+	}
+
+	module.exports = code => errname(uv, code);
+}
+
+// Used for testing the fallback behavior
+module.exports.__test__ = errname;
+
+function errname(uv, code) {
+	if (uv) {
+		return uv.errname(code);
+	}
+
+	if (!(code < 0)) {
+		throw new Error('err >= 0');
+	}
+
+	return `Unknown system error ${code}`;
+}
+
+
+
+/***/ }),
+
+/***/ 430:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = octokitValidate;
+
+const validate = __webpack_require__(348);
+
+function octokitValidate(octokit) {
+  octokit.hook.before("request", validate.bind(null, octokit));
+}
+
+
+/***/ }),
+
+/***/ 431:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const os = __importStar(__webpack_require__(87));
+/**
+ * Commands
+ *
+ * Command Format:
+ *   ::name key=value,key=value::message
+ *
+ * Examples:
+ *   ::warning::This is the message
+ *   ::set-env name=MY_VAR::some value
+ */
+function issueCommand(command, properties, message) {
+    const cmd = new Command(command, properties, message);
+    process.stdout.write(cmd.toString() + os.EOL);
+}
+exports.issueCommand = issueCommand;
+function issue(name, message = '') {
+    issueCommand(name, {}, message);
+}
+exports.issue = issue;
+const CMD_STRING = '::';
+class Command {
+    constructor(command, properties, message) {
+        if (!command) {
+            command = 'missing.command';
+        }
+        this.command = command;
+        this.properties = properties;
+        this.message = message;
+    }
+    toString() {
+        let cmdStr = CMD_STRING + this.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 += ',';
+                        }
+                        cmdStr += `${key}=${escapeProperty(val)}`;
+                    }
+                }
+            }
+        }
+        cmdStr += `${CMD_STRING}${escapeData(this.message)}`;
+        return cmdStr;
+    }
+}
+function escapeData(s) {
+    return (s || '')
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A');
+}
+function escapeProperty(s) {
+    return (s || '')
+        .replace(/%/g, '%25')
+        .replace(/\r/g, '%0D')
+        .replace(/\n/g, '%0A')
+        .replace(/:/g, '%3A')
+        .replace(/,/g, '%2C');
+}
+//# sourceMappingURL=command.js.map
+
+/***/ }),
+
+/***/ 453:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+var once = __webpack_require__(969)
+var eos = __webpack_require__(9)
+var fs = __webpack_require__(747) // we only need fs to get the ReadStream and WriteStream prototypes
+
+var noop = function () {}
+var ancient = /^v?\.0/.test(process.version)
+
+var isFn = function (fn) {
+  return typeof fn === 'function'
+}
+
+var isFS = function (stream) {
+  if (!ancient) return false // newer node version do not need to care about fs is a special way
+  if (!fs) return false // browser
+  return (stream instanceof (fs.ReadStream || noop) || stream instanceof (fs.WriteStream || noop)) && isFn(stream.close)
+}
+
+var isRequest = function (stream) {
+  return stream.setHeader && isFn(stream.abort)
+}
+
+var destroyer = function (stream, reading, writing, callback) {
+  callback = once(callback)
+
+  var closed = false
+  stream.on('close', function () {
+    closed = true
+  })
+
+  eos(stream, {readable: reading, writable: writing}, function (err) {
+    if (err) return callback(err)
+    closed = true
+    callback()
+  })
+
+  var destroyed = false
+  return function (err) {
+    if (closed) return
+    if (destroyed) return
+    destroyed = true
+
+    if (isFS(stream)) return stream.close(noop) // use close for fs streams to avoid fd leaks
+    if (isRequest(stream)) return stream.abort() // request.destroy just do .end - .abort is what we want
+
+    if (isFn(stream.destroy)) return stream.destroy()
+
+    callback(err || new Error('stream was destroyed'))
+  }
+}
+
+var call = function (fn) {
+  fn()
+}
+
+var pipe = function (from, to) {
+  return from.pipe(to)
+}
+
+var pump = function () {
+  var streams = Array.prototype.slice.call(arguments)
+  var callback = isFn(streams[streams.length - 1] || noop) && streams.pop() || noop
+
+  if (Array.isArray(streams[0])) streams = streams[0]
+  if (streams.length < 2) throw new Error('pump requires two streams per minimum')
+
+  var error
+  var destroys = streams.map(function (stream, i) {
+    var reading = i < streams.length - 1
+    var writing = i > 0
+    return destroyer(stream, reading, writing, function (err) {
+      if (!error) error = err
+      if (err) destroys.forEach(call)
+      if (reading) return
+      destroys.forEach(call)
+      callback(error)
+    })
+  })
+
+  return streams.reduce(pipe)
+}
+
+module.exports = pump
+
+
+/***/ }),
+
+/***/ 462:
+/***/ (function(module) {
+
+"use strict";
+
+
+// See http://www.robvanderwoude.com/escapechars.php
+const metaCharsRegExp = /([()\][%!^"`<>&|;, *?])/g;
+
+function escapeCommand(arg) {
+    // Escape meta chars
+    arg = arg.replace(metaCharsRegExp, '^$1');
+
+    return arg;
+}
+
+function escapeArgument(arg, doubleEscapeMetaChars) {
+    // Convert to string
+    arg = `${arg}`;
+
+    // Algorithm below is based on https://qntm.org/cmd
+
+    // Sequence of backslashes followed by a double quote:
+    // double up all the backslashes and escape the double quote
+    arg = arg.replace(/(\\*)"/g, '$1$1\\"');
+
+    // Sequence of backslashes followed by the end of the string
+    // (which will become a double quote later):
+    // double up all the backslashes
+    arg = arg.replace(/(\\*)$/, '$1$1');
+
+    // All other backslashes occur literally
+
+    // Quote the whole thing:
+    arg = `"${arg}"`;
+
+    // Escape meta chars
+    arg = arg.replace(metaCharsRegExp, '^$1');
+
+    // Double escape meta chars if necessary
+    if (doubleEscapeMetaChars) {
+        arg = arg.replace(metaCharsRegExp, '^$1');
+    }
+
+    return arg;
+}
+
+module.exports.command = escapeCommand;
+module.exports.argument = escapeArgument;
+
+
+/***/ }),
+
+/***/ 463:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var deprecation = __webpack_require__(692);
+var once = _interopDefault(__webpack_require__(969));
+
+const logOnce = once(deprecation => console.warn(deprecation));
+/**
+ * Error with extra properties to help with debugging
+ */
+
+class RequestError extends Error {
+  constructor(message, statusCode, options) {
+    super(message); // Maintains proper stack trace (only available on V8)
+
+    /* istanbul ignore next */
+
+    if (Error.captureStackTrace) {
+      Error.captureStackTrace(this, this.constructor);
+    }
+
+    this.name = "HttpError";
+    this.status = statusCode;
+    Object.defineProperty(this, "code", {
+      get() {
+        logOnce(new deprecation.Deprecation("[@octokit/request-error] `error.code` is deprecated, use `error.status`."));
+        return statusCode;
+      }
+
+    });
+    this.headers = options.headers || {}; // redact request credentials without mutating original request options
+
+    const requestCopy = Object.assign({}, options.request);
+
+    if (options.request.headers.authorization) {
+      requestCopy.headers = Object.assign({}, options.request.headers, {
+        authorization: options.request.headers.authorization.replace(/ .*$/, " [REDACTED]")
+      });
+    }
+
+    requestCopy.url = requestCopy.url // client_id & client_secret can be passed as URL query parameters to increase rate limit
+    // see https://developer.github.com/v3/#increasing-the-unauthenticated-rate-limit-for-oauth-applications
+    .replace(/\bclient_secret=\w+/g, "client_secret=[REDACTED]") // OAuth tokens can be passed as URL query parameters, although it is not recommended
+    // see https://developer.github.com/v3/#oauth2-token-sent-in-a-header
+    .replace(/\baccess_token=\w+/g, "access_token=[REDACTED]");
+    this.request = requestCopy;
+  }
+
+}
+
+exports.RequestError = RequestError;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 469:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __importDefault = (this && this.__importDefault) || function (mod) {
+    return (mod && mod.__esModule) ? mod : { "default": mod };
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+// Originally pulled from https://github.com/JasonEtco/actions-toolkit/blob/master/src/github.ts
+const graphql_1 = __webpack_require__(898);
+const rest_1 = __importDefault(__webpack_require__(0));
+const Context = __importStar(__webpack_require__(262));
+const httpClient = __importStar(__webpack_require__(539));
+// We need this in order to extend Octokit
+rest_1.default.prototype = new rest_1.default();
+exports.context = new Context.Context();
+class GitHub extends rest_1.default {
+    constructor(token, opts) {
+        super(GitHub.getOctokitOptions(GitHub.disambiguate(token, opts)));
+        this.graphql = GitHub.getGraphQL(GitHub.disambiguate(token, opts));
+    }
+    /**
+     * Disambiguates the constructor overload parameters
+     */
+    static disambiguate(token, opts) {
+        return [
+            typeof token === 'string' ? token : '',
+            typeof token === 'object' ? token : opts || {}
+        ];
+    }
+    static getOctokitOptions(args) {
+        const token = args[0];
+        const options = Object.assign({}, 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 ? Object.assign({}, options.request) : {};
+            // Set the agent
+            options.request.agent = agent;
+        }
+        return options;
+    }
+    static getGraphQL(args) {
+        const defaults = {};
+        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_1.graphql.defaults(defaults);
+    }
+    static getAuthString(token, options) {
+        // 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}`;
+    }
+    static getProxyAgent(options) {
+        var _a;
+        if (!((_a = options.request) === null || _a === void 0 ? void 0 : _a.agent)) {
+            const serverUrl = 'https://api.github.com';
+            if (httpClient.getProxyUrl(serverUrl)) {
+                const hc = new httpClient.HttpClient();
+                return hc.getAgent(serverUrl);
+            }
+        }
+        return undefined;
+    }
+}
+exports.GitHub = GitHub;
+//# sourceMappingURL=github.js.map
+
+/***/ }),
+
+/***/ 470:
+/***/ (function(module, exports) {
+
+exports = module.exports = SemVer
+
+var debug
+/* istanbul ignore next */
+if (typeof process === 'object' &&
+    process.env &&
+    process.env.NODE_DEBUG &&
+    /\bsemver\b/i.test(process.env.NODE_DEBUG)) {
+  debug = function () {
+    var args = Array.prototype.slice.call(arguments, 0)
+    args.unshift('SEMVER')
+    console.log.apply(console, args)
+  }
+} else {
+  debug = function () {}
+}
+
+// Note: this is the semver.org version of the spec that it implements
+// Not necessarily the package version of this code.
+exports.SEMVER_SPEC_VERSION = '2.0.0'
+
+var MAX_LENGTH = 256
+var MAX_SAFE_INTEGER = Number.MAX_SAFE_INTEGER ||
+  /* istanbul ignore next */ 9007199254740991
+
+// Max safe segment length for coercion.
+var MAX_SAFE_COMPONENT_LENGTH = 16
+
+// The actual regexps go on exports.re
+var re = exports.re = []
+var src = exports.src = []
+var R = 0
+
+// The following Regular Expressions can be used for tokenizing,
+// validating, and parsing SemVer version strings.
+
+// ## Numeric Identifier
+// A single `0`, or a non-zero digit followed by zero or more digits.
+
+var NUMERICIDENTIFIER = R++
+src[NUMERICIDENTIFIER] = '0|[1-9]\\d*'
+var NUMERICIDENTIFIERLOOSE = R++
+src[NUMERICIDENTIFIERLOOSE] = '[0-9]+'
+
+// ## Non-numeric Identifier
+// Zero or more digits, followed by a letter or hyphen, and then zero or
+// more letters, digits, or hyphens.
+
+var NONNUMERICIDENTIFIER = R++
+src[NONNUMERICIDENTIFIER] = '\\d*[a-zA-Z-][a-zA-Z0-9-]*'
+
+// ## Main Version
+// Three dot-separated numeric identifiers.
+
+var MAINVERSION = R++
+src[MAINVERSION] = '(' + src[NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[NUMERICIDENTIFIER] + ')\\.' +
+                   '(' + src[NUMERICIDENTIFIER] + ')'
+
+var MAINVERSIONLOOSE = R++
+src[MAINVERSIONLOOSE] = '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[NUMERICIDENTIFIERLOOSE] + ')\\.' +
+                        '(' + src[NUMERICIDENTIFIERLOOSE] + ')'
+
+// ## Pre-release Version Identifier
+// A numeric identifier, or a non-numeric identifier.
+
+var PRERELEASEIDENTIFIER = R++
+src[PRERELEASEIDENTIFIER] = '(?:' + src[NUMERICIDENTIFIER] +
+                            '|' + src[NONNUMERICIDENTIFIER] + ')'
+
+var PRERELEASEIDENTIFIERLOOSE = R++
+src[PRERELEASEIDENTIFIERLOOSE] = '(?:' + src[NUMERICIDENTIFIERLOOSE] +
+                                 '|' + src[NONNUMERICIDENTIFIER] + ')'
+
+// ## Pre-release Version
+// Hyphen, followed by one or more dot-separated pre-release version
+// identifiers.
+
+var PRERELEASE = R++
+src[PRERELEASE] = '(?:-(' + src[PRERELEASEIDENTIFIER] +
+                  '(?:\\.' + src[PRERELEASEIDENTIFIER] + ')*))'
+
+var PRERELEASELOOSE = R++
+src[PRERELEASELOOSE] = '(?:-?(' + src[PRERELEASEIDENTIFIERLOOSE] +
+                       '(?:\\.' + src[PRERELEASEIDENTIFIERLOOSE] + ')*))'
+
+// ## Build Metadata Identifier
+// Any combination of digits, letters, or hyphens.
+
+var BUILDIDENTIFIER = R++
+src[BUILDIDENTIFIER] = '[0-9A-Za-z-]+'
+
+// ## Build Metadata
+// Plus sign, followed by one or more period-separated build metadata
+// identifiers.
+
+var BUILD = R++
+src[BUILD] = '(?:\\+(' + src[BUILDIDENTIFIER] +
+             '(?:\\.' + src[BUILDIDENTIFIER] + ')*))'
+
+// ## Full Version String
+// A main version, followed optionally by a pre-release version and
+// build metadata.
+
+// Note that the only major, minor, patch, and pre-release sections of
+// the version string are capturing groups.  The build metadata is not a
+// capturing group, because it should not ever be used in version
+// comparison.
+
+var FULL = R++
+var FULLPLAIN = 'v?' + src[MAINVERSION] +
+                src[PRERELEASE] + '?' +
+                src[BUILD] + '?'
+
+src[FULL] = '^' + FULLPLAIN + '$'
+
+// like full, but allows v1.2.3 and =1.2.3, which people do sometimes.
+// also, 1.0.0alpha1 (prerelease without the hyphen) which is pretty
+// common in the npm registry.
+var LOOSEPLAIN = '[v=\\s]*' + src[MAINVERSIONLOOSE] +
+                 src[PRERELEASELOOSE] + '?' +
+                 src[BUILD] + '?'
+
+var LOOSE = R++
+src[LOOSE] = '^' + LOOSEPLAIN + '$'
+
+var GTLT = R++
+src[GTLT] = '((?:<|>)?=?)'
+
+// Something like "2.*" or "1.2.x".
+// Note that "x.x" is a valid xRange identifer, meaning "any version"
+// Only the first item is strictly required.
+var XRANGEIDENTIFIERLOOSE = R++
+src[XRANGEIDENTIFIERLOOSE] = src[NUMERICIDENTIFIERLOOSE] + '|x|X|\\*'
+var XRANGEIDENTIFIER = R++
+src[XRANGEIDENTIFIER] = src[NUMERICIDENTIFIER] + '|x|X|\\*'
+
+var XRANGEPLAIN = R++
+src[XRANGEPLAIN] = '[v=\\s]*(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:\\.(' + src[XRANGEIDENTIFIER] + ')' +
+                   '(?:' + src[PRERELEASE] + ')?' +
+                   src[BUILD] + '?' +
+                   ')?)?'
+
+var XRANGEPLAINLOOSE = R++
+src[XRANGEPLAINLOOSE] = '[v=\\s]*(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:\\.(' + src[XRANGEIDENTIFIERLOOSE] + ')' +
+                        '(?:' + src[PRERELEASELOOSE] + ')?' +
+                        src[BUILD] + '?' +
+                        ')?)?'
+
+var XRANGE = R++
+src[XRANGE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAIN] + '$'
+var XRANGELOOSE = R++
+src[XRANGELOOSE] = '^' + src[GTLT] + '\\s*' + src[XRANGEPLAINLOOSE] + '$'
+
+// Coercion.
+// Extract anything that could conceivably be a part of a valid semver
+var COERCE = R++
+src[COERCE] = '(?:^|[^\\d])' +
+              '(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '})' +
+              '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' +
+              '(?:\\.(\\d{1,' + MAX_SAFE_COMPONENT_LENGTH + '}))?' +
+              '(?:$|[^\\d])'
+
+// Tilde ranges.
+// Meaning is "reasonably at or greater than"
+var LONETILDE = R++
+src[LONETILDE] = '(?:~>?)'
+
+var TILDETRIM = R++
+src[TILDETRIM] = '(\\s*)' + src[LONETILDE] + '\\s+'
+re[TILDETRIM] = new RegExp(src[TILDETRIM], 'g')
+var tildeTrimReplace = '$1~'
+
+var TILDE = R++
+src[TILDE] = '^' + src[LONETILDE] + src[XRANGEPLAIN] + '$'
+var TILDELOOSE = R++
+src[TILDELOOSE] = '^' + src[LONETILDE] + src[XRANGEPLAINLOOSE] + '$'
+
+// Caret ranges.
+// Meaning is "at least and backwards compatible with"
+var LONECARET = R++
+src[LONECARET] = '(?:\\^)'
+
+var CARETTRIM = R++
+src[CARETTRIM] = '(\\s*)' + src[LONECARET] + '\\s+'
+re[CARETTRIM] = new RegExp(src[CARETTRIM], 'g')
+var caretTrimReplace = '$1^'
+
+var CARET = R++
+src[CARET] = '^' + src[LONECARET] + src[XRANGEPLAIN] + '$'
+var CARETLOOSE = R++
+src[CARETLOOSE] = '^' + src[LONECARET] + src[XRANGEPLAINLOOSE] + '$'
+
+// A simple gt/lt/eq thing, or just "" to indicate "any version"
+var COMPARATORLOOSE = R++
+src[COMPARATORLOOSE] = '^' + src[GTLT] + '\\s*(' + LOOSEPLAIN + ')$|^$'
+var COMPARATOR = R++
+src[COMPARATOR] = '^' + src[GTLT] + '\\s*(' + FULLPLAIN + ')$|^$'
+
+// An expression to strip any whitespace between the gtlt and the thing
+// it modifies, so that `> 1.2.3` ==> `>1.2.3`
+var COMPARATORTRIM = R++
+src[COMPARATORTRIM] = '(\\s*)' + src[GTLT] +
+                      '\\s*(' + LOOSEPLAIN + '|' + src[XRANGEPLAIN] + ')'
+
+// this one has to use the /g flag
+re[COMPARATORTRIM] = new RegExp(src[COMPARATORTRIM], 'g')
+var comparatorTrimReplace = '$1$2$3'
+
+// Something like `1.2.3 - 1.2.4`
+// Note that these all use the loose form, because they'll be
+// checked against either the strict or loose comparator form
+// later.
+var HYPHENRANGE = R++
+src[HYPHENRANGE] = '^\\s*(' + src[XRANGEPLAIN] + ')' +
+                   '\\s+-\\s+' +
+                   '(' + src[XRANGEPLAIN] + ')' +
+                   '\\s*$'
+
+var HYPHENRANGELOOSE = R++
+src[HYPHENRANGELOOSE] = '^\\s*(' + src[XRANGEPLAINLOOSE] + ')' +
+                        '\\s+-\\s+' +
+                        '(' + src[XRANGEPLAINLOOSE] + ')' +
+                        '\\s*$'
+
+// Star ranges basically just allow anything at all.
+var STAR = R++
+src[STAR] = '(<|>)?=?\\s*\\*'
+
+// Compile to actual regexp objects.
+// All are flag-free, unless they were created above with a flag.
+for (var i = 0; i < R; i++) {
+  debug(i, src[i])
+  if (!re[i]) {
+    re[i] = new RegExp(src[i])
+  }
+}
+
+exports.parse = parse
+function parse (version, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  if (version.length > MAX_LENGTH) {
+    return null
+  }
+
+  var r = options.loose ? re[LOOSE] : re[FULL]
+  if (!r.test(version)) {
+    return null
+  }
+
+  try {
+    return new SemVer(version, options)
+  } catch (er) {
+    return null
+  }
+}
+
+exports.valid = valid
+function valid (version, options) {
+  var v = parse(version, options)
+  return v ? v.version : null
+}
+
+exports.clean = clean
+function clean (version, options) {
+  var s = parse(version.trim().replace(/^[=v]+/, ''), options)
+  return s ? s.version : null
+}
+
+exports.SemVer = SemVer
+
+function SemVer (version, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+  if (version instanceof SemVer) {
+    if (version.loose === options.loose) {
+      return version
+    } else {
+      version = version.version
+    }
+  } else if (typeof version !== 'string') {
+    throw new TypeError('Invalid Version: ' + version)
+  }
+
+  if (version.length > MAX_LENGTH) {
+    throw new TypeError('version is longer than ' + MAX_LENGTH + ' characters')
+  }
+
+  if (!(this instanceof SemVer)) {
+    return new SemVer(version, options)
+  }
+
+  debug('SemVer', version, options)
+  this.options = options
+  this.loose = !!options.loose
+
+  var m = version.trim().match(options.loose ? re[LOOSE] : re[FULL])
+
+  if (!m) {
+    throw new TypeError('Invalid Version: ' + version)
+  }
+
+  this.raw = version
+
+  // these are actually numbers
+  this.major = +m[1]
+  this.minor = +m[2]
+  this.patch = +m[3]
+
+  if (this.major > MAX_SAFE_INTEGER || this.major < 0) {
+    throw new TypeError('Invalid major version')
+  }
+
+  if (this.minor > MAX_SAFE_INTEGER || this.minor < 0) {
+    throw new TypeError('Invalid minor version')
+  }
+
+  if (this.patch > MAX_SAFE_INTEGER || this.patch < 0) {
+    throw new TypeError('Invalid patch version')
+  }
+
+  // numberify any prerelease numeric ids
+  if (!m[4]) {
+    this.prerelease = []
+  } else {
+    this.prerelease = m[4].split('.').map(function (id) {
+      if (/^[0-9]+$/.test(id)) {
+        var num = +id
+        if (num >= 0 && num < MAX_SAFE_INTEGER) {
+          return num
+        }
+      }
+      return id
+    })
+  }
+
+  this.build = m[5] ? m[5].split('.') : []
+  this.format()
+}
+
+SemVer.prototype.format = function () {
+  this.version = this.major + '.' + this.minor + '.' + this.patch
+  if (this.prerelease.length) {
+    this.version += '-' + this.prerelease.join('.')
+  }
+  return this.version
+}
+
+SemVer.prototype.toString = function () {
+  return this.version
+}
+
+SemVer.prototype.compare = function (other) {
+  debug('SemVer.compare', this.version, this.options, other)
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  return this.compareMain(other) || this.comparePre(other)
+}
+
+SemVer.prototype.compareMain = function (other) {
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  return compareIdentifiers(this.major, other.major) ||
+         compareIdentifiers(this.minor, other.minor) ||
+         compareIdentifiers(this.patch, other.patch)
+}
+
+SemVer.prototype.comparePre = function (other) {
+  if (!(other instanceof SemVer)) {
+    other = new SemVer(other, this.options)
+  }
+
+  // NOT having a prerelease is > having one
+  if (this.prerelease.length && !other.prerelease.length) {
+    return -1
+  } else if (!this.prerelease.length && other.prerelease.length) {
+    return 1
+  } else if (!this.prerelease.length && !other.prerelease.length) {
+    return 0
+  }
+
+  var i = 0
+  do {
+    var a = this.prerelease[i]
+    var b = other.prerelease[i]
+    debug('prerelease compare', i, a, b)
+    if (a === undefined && b === undefined) {
+      return 0
+    } else if (b === undefined) {
+      return 1
+    } else if (a === undefined) {
+      return -1
+    } else if (a === b) {
+      continue
+    } else {
+      return compareIdentifiers(a, b)
+    }
+  } while (++i)
+}
+
+// preminor will bump the version up to the next minor release, and immediately
+// down to pre-release. premajor and prepatch work the same way.
+SemVer.prototype.inc = function (release, identifier) {
+  switch (release) {
+    case 'premajor':
+      this.prerelease.length = 0
+      this.patch = 0
+      this.minor = 0
+      this.major++
+      this.inc('pre', identifier)
+      break
+    case 'preminor':
+      this.prerelease.length = 0
+      this.patch = 0
+      this.minor++
+      this.inc('pre', identifier)
+      break
+    case 'prepatch':
+      // If this is already a prerelease, it will bump to the next version
+      // drop any prereleases that might already exist, since they are not
+      // relevant at this point.
+      this.prerelease.length = 0
+      this.inc('patch', identifier)
+      this.inc('pre', identifier)
+      break
+    // If the input is a non-prerelease version, this acts the same as
+    // prepatch.
+    case 'prerelease':
+      if (this.prerelease.length === 0) {
+        this.inc('patch', identifier)
+      }
+      this.inc('pre', identifier)
+      break
+
+    case 'major':
+      // If this is a pre-major version, bump up to the same major version.
+      // Otherwise increment major.
+      // 1.0.0-5 bumps to 1.0.0
+      // 1.1.0 bumps to 2.0.0
+      if (this.minor !== 0 ||
+          this.patch !== 0 ||
+          this.prerelease.length === 0) {
+        this.major++
+      }
+      this.minor = 0
+      this.patch = 0
+      this.prerelease = []
+      break
+    case 'minor':
+      // If this is a pre-minor version, bump up to the same minor version.
+      // Otherwise increment minor.
+      // 1.2.0-5 bumps to 1.2.0
+      // 1.2.1 bumps to 1.3.0
+      if (this.patch !== 0 || this.prerelease.length === 0) {
+        this.minor++
+      }
+      this.patch = 0
+      this.prerelease = []
+      break
+    case 'patch':
+      // If this is not a pre-release version, it will increment the patch.
+      // If it is a pre-release it will bump up to the same patch version.
+      // 1.2.0-5 patches to 1.2.0
+      // 1.2.0 patches to 1.2.1
+      if (this.prerelease.length === 0) {
+        this.patch++
+      }
+      this.prerelease = []
+      break
+    // This probably shouldn't be used publicly.
+    // 1.0.0 "pre" would become 1.0.0-0 which is the wrong direction.
+    case 'pre':
+      if (this.prerelease.length === 0) {
+        this.prerelease = [0]
+      } else {
+        var i = this.prerelease.length
+        while (--i >= 0) {
+          if (typeof this.prerelease[i] === 'number') {
+            this.prerelease[i]++
+            i = -2
+          }
+        }
+        if (i === -1) {
+          // didn't increment anything
+          this.prerelease.push(0)
+        }
+      }
+      if (identifier) {
+        // 1.2.0-beta.1 bumps to 1.2.0-beta.2,
+        // 1.2.0-beta.fooblz or 1.2.0-beta bumps to 1.2.0-beta.0
+        if (this.prerelease[0] === identifier) {
+          if (isNaN(this.prerelease[1])) {
+            this.prerelease = [identifier, 0]
+          }
+        } else {
+          this.prerelease = [identifier, 0]
+        }
+      }
+      break
+
+    default:
+      throw new Error('invalid increment argument: ' + release)
+  }
+  this.format()
+  this.raw = this.version
+  return this
+}
+
+exports.inc = inc
+function inc (version, release, loose, identifier) {
+  if (typeof (loose) === 'string') {
+    identifier = loose
+    loose = undefined
+  }
+
+  try {
+    return new SemVer(version, loose).inc(release, identifier).version
+  } catch (er) {
+    return null
+  }
+}
+
+exports.diff = diff
+function diff (version1, version2) {
+  if (eq(version1, version2)) {
+    return null
+  } else {
+    var v1 = parse(version1)
+    var v2 = parse(version2)
+    var prefix = ''
+    if (v1.prerelease.length || v2.prerelease.length) {
+      prefix = 'pre'
+      var defaultResult = 'prerelease'
+    }
+    for (var key in v1) {
+      if (key === 'major' || key === 'minor' || key === 'patch') {
+        if (v1[key] !== v2[key]) {
+          return prefix + key
+        }
+      }
+    }
+    return defaultResult // may be undefined
+  }
+}
+
+exports.compareIdentifiers = compareIdentifiers
+
+var numeric = /^[0-9]+$/
+function compareIdentifiers (a, b) {
+  var anum = numeric.test(a)
+  var bnum = numeric.test(b)
+
+  if (anum && bnum) {
+    a = +a
+    b = +b
+  }
+
+  return a === b ? 0
+    : (anum && !bnum) ? -1
+    : (bnum && !anum) ? 1
+    : a < b ? -1
+    : 1
+}
+
+exports.rcompareIdentifiers = rcompareIdentifiers
+function rcompareIdentifiers (a, b) {
+  return compareIdentifiers(b, a)
+}
+
+exports.major = major
+function major (a, loose) {
+  return new SemVer(a, loose).major
+}
+
+exports.minor = minor
+function minor (a, loose) {
+  return new SemVer(a, loose).minor
+}
+
+exports.patch = patch
+function patch (a, loose) {
+  return new SemVer(a, loose).patch
+}
+
+exports.compare = compare
+function compare (a, b, loose) {
+  return new SemVer(a, loose).compare(new SemVer(b, loose))
+}
+
+exports.compareLoose = compareLoose
+function compareLoose (a, b) {
+  return compare(a, b, true)
+}
+
+exports.rcompare = rcompare
+function rcompare (a, b, loose) {
+  return compare(b, a, loose)
+}
+
+exports.sort = sort
+function sort (list, loose) {
+  return list.sort(function (a, b) {
+    return exports.compare(a, b, loose)
+  })
+}
+
+exports.rsort = rsort
+function rsort (list, loose) {
+  return list.sort(function (a, b) {
+    return exports.rcompare(a, b, loose)
+  })
+}
+
+exports.gt = gt
+function gt (a, b, loose) {
+  return compare(a, b, loose) > 0
+}
+
+exports.lt = lt
+function lt (a, b, loose) {
+  return compare(a, b, loose) < 0
+}
+
+exports.eq = eq
+function eq (a, b, loose) {
+  return compare(a, b, loose) === 0
+}
+
+exports.neq = neq
+function neq (a, b, loose) {
+  return compare(a, b, loose) !== 0
+}
+
+exports.gte = gte
+function gte (a, b, loose) {
+  return compare(a, b, loose) >= 0
+}
+
+exports.lte = lte
+function lte (a, b, loose) {
+  return compare(a, b, loose) <= 0
+}
+
+exports.cmp = cmp
+function cmp (a, op, b, loose) {
+  switch (op) {
+    case '===':
+      if (typeof a === 'object')
+        a = a.version
+      if (typeof b === 'object')
+        b = b.version
+      return a === b
+
+    case '!==':
+      if (typeof a === 'object')
+        a = a.version
+      if (typeof b === 'object')
+        b = b.version
+      return a !== b
+
+    case '':
+    case '=':
+    case '==':
+      return eq(a, b, loose)
+
+    case '!=':
+      return neq(a, b, loose)
+
+    case '>':
+      return gt(a, b, loose)
+
+    case '>=':
+      return gte(a, b, loose)
+
+    case '<':
+      return lt(a, b, loose)
+
+    case '<=':
+      return lte(a, b, loose)
+
+    default:
+      throw new TypeError('Invalid operator: ' + op)
+  }
+}
+
+exports.Comparator = Comparator
+function Comparator (comp, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (comp instanceof Comparator) {
+    if (comp.loose === !!options.loose) {
+      return comp
+    } else {
+      comp = comp.value
+    }
+  }
+
+  if (!(this instanceof Comparator)) {
+    return new Comparator(comp, options)
+  }
+
+  debug('comparator', comp, options)
+  this.options = options
+  this.loose = !!options.loose
+  this.parse(comp)
+
+  if (this.semver === ANY) {
+    this.value = ''
+  } else {
+    this.value = this.operator + this.semver.version
+  }
+
+  debug('comp', this)
+}
+
+var ANY = {}
+Comparator.prototype.parse = function (comp) {
+  var r = this.options.loose ? re[COMPARATORLOOSE] : re[COMPARATOR]
+  var m = comp.match(r)
+
+  if (!m) {
+    throw new TypeError('Invalid comparator: ' + comp)
+  }
+
+  this.operator = m[1]
+  if (this.operator === '=') {
+    this.operator = ''
+  }
+
+  // if it literally is just '>' or '' then allow anything.
+  if (!m[2]) {
+    this.semver = ANY
+  } else {
+    this.semver = new SemVer(m[2], this.options.loose)
+  }
+}
+
+Comparator.prototype.toString = function () {
+  return this.value
+}
+
+Comparator.prototype.test = function (version) {
+  debug('Comparator.test', version, this.options.loose)
+
+  if (this.semver === ANY) {
+    return true
+  }
+
+  if (typeof version === 'string') {
+    version = new SemVer(version, this.options)
+  }
+
+  return cmp(version, this.operator, this.semver, this.options)
+}
+
+Comparator.prototype.intersects = function (comp, options) {
+  if (!(comp instanceof Comparator)) {
+    throw new TypeError('a Comparator is required')
+  }
+
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  var rangeTmp
+
+  if (this.operator === '') {
+    rangeTmp = new Range(comp.value, options)
+    return satisfies(this.value, rangeTmp, options)
+  } else if (comp.operator === '') {
+    rangeTmp = new Range(this.value, options)
+    return satisfies(comp.semver, rangeTmp, options)
+  }
+
+  var sameDirectionIncreasing =
+    (this.operator === '>=' || this.operator === '>') &&
+    (comp.operator === '>=' || comp.operator === '>')
+  var sameDirectionDecreasing =
+    (this.operator === '<=' || this.operator === '<') &&
+    (comp.operator === '<=' || comp.operator === '<')
+  var sameSemVer = this.semver.version === comp.semver.version
+  var differentDirectionsInclusive =
+    (this.operator === '>=' || this.operator === '<=') &&
+    (comp.operator === '>=' || comp.operator === '<=')
+  var oppositeDirectionsLessThan =
+    cmp(this.semver, '<', comp.semver, options) &&
+    ((this.operator === '>=' || this.operator === '>') &&
+    (comp.operator === '<=' || comp.operator === '<'))
+  var oppositeDirectionsGreaterThan =
+    cmp(this.semver, '>', comp.semver, options) &&
+    ((this.operator === '<=' || this.operator === '<') &&
+    (comp.operator === '>=' || comp.operator === '>'))
+
+  return sameDirectionIncreasing || sameDirectionDecreasing ||
+    (sameSemVer && differentDirectionsInclusive) ||
+    oppositeDirectionsLessThan || oppositeDirectionsGreaterThan
+}
+
+exports.Range = Range
+function Range (range, options) {
+  if (!options || typeof options !== 'object') {
+    options = {
+      loose: !!options,
+      includePrerelease: false
+    }
+  }
+
+  if (range instanceof Range) {
+    if (range.loose === !!options.loose &&
+        range.includePrerelease === !!options.includePrerelease) {
+      return range
+    } else {
+      return new Range(range.raw, options)
+    }
+  }
+
+  if (range instanceof Comparator) {
+    return new Range(range.value, options)
+  }
+
+  if (!(this instanceof Range)) {
+    return new Range(range, options)
+  }
+
+  this.options = options
+  this.loose = !!options.loose
+  this.includePrerelease = !!options.includePrerelease
+
+  // First, split based on boolean or ||
+  this.raw = range
+  this.set = range.split(/\s*\|\|\s*/).map(function (range) {
+    return this.parseRange(range.trim())
+  }, this).filter(function (c) {
+    // throw out any that are not relevant for whatever reason
+    return c.length
+  })
+
+  if (!this.set.length) {
+    throw new TypeError('Invalid SemVer Range: ' + range)
+  }
+
+  this.format()
+}
+
+Range.prototype.format = function () {
+  this.range = this.set.map(function (comps) {
+    return comps.join(' ').trim()
+  }).join('||').trim()
+  return this.range
+}
+
+Range.prototype.toString = function () {
+  return this.range
+}
+
+Range.prototype.parseRange = function (range) {
+  var loose = this.options.loose
+  range = range.trim()
+  // `1.2.3 - 1.2.4` => `>=1.2.3 <=1.2.4`
+  var hr = loose ? re[HYPHENRANGELOOSE] : re[HYPHENRANGE]
+  range = range.replace(hr, hyphenReplace)
+  debug('hyphen replace', range)
+  // `> 1.2.3 < 1.2.5` => `>1.2.3 <1.2.5`
+  range = range.replace(re[COMPARATORTRIM], comparatorTrimReplace)
+  debug('comparator trim', range, re[COMPARATORTRIM])
+
+  // `~ 1.2.3` => `~1.2.3`
+  range = range.replace(re[TILDETRIM], tildeTrimReplace)
+
+  // `^ 1.2.3` => `^1.2.3`
+  range = range.replace(re[CARETTRIM], caretTrimReplace)
+
+  // normalize spaces
+  range = range.split(/\s+/).join(' ')
+
+  // At this point, the range is completely trimmed and
+  // ready to be split into comparators.
+
+  var compRe = loose ? re[COMPARATORLOOSE] : re[COMPARATOR]
+  var set = range.split(' ').map(function (comp) {
+    return parseComparator(comp, this.options)
+  }, this).join(' ').split(/\s+/)
+  if (this.options.loose) {
+    // in loose mode, throw out any that are not valid comparators
+    set = set.filter(function (comp) {
+      return !!comp.match(compRe)
+    })
+  }
+  set = set.map(function (comp) {
+    return new Comparator(comp, this.options)
+  }, this)
+
+  return set
+}
+
+Range.prototype.intersects = function (range, options) {
+  if (!(range instanceof Range)) {
+    throw new TypeError('a Range is required')
+  }
+
+  return this.set.some(function (thisComparators) {
+    return thisComparators.every(function (thisComparator) {
+      return range.set.some(function (rangeComparators) {
+        return rangeComparators.every(function (rangeComparator) {
+          return thisComparator.intersects(rangeComparator, options)
+        })
+      })
+    })
+  })
+}
+
+// Mostly just for testing and legacy API reasons
+exports.toComparators = toComparators
+function toComparators (range, options) {
+  return new Range(range, options).set.map(function (comp) {
+    return comp.map(function (c) {
+      return c.value
+    }).join(' ').trim().split(' ')
+  })
+}
+
+// comprised of xranges, tildes, stars, and gtlt's at this point.
+// already replaced the hyphen ranges
+// turn into a set of JUST comparators.
+function parseComparator (comp, options) {
+  debug('comp', comp, options)
+  comp = replaceCarets(comp, options)
+  debug('caret', comp)
+  comp = replaceTildes(comp, options)
+  debug('tildes', comp)
+  comp = replaceXRanges(comp, options)
+  debug('xrange', comp)
+  comp = replaceStars(comp, options)
+  debug('stars', comp)
+  return comp
+}
+
+function isX (id) {
+  return !id || id.toLowerCase() === 'x' || id === '*'
+}
+
+// ~, ~> --> * (any, kinda silly)
+// ~2, ~2.x, ~2.x.x, ~>2, ~>2.x ~>2.x.x --> >=2.0.0 <3.0.0
+// ~2.0, ~2.0.x, ~>2.0, ~>2.0.x --> >=2.0.0 <2.1.0
+// ~1.2, ~1.2.x, ~>1.2, ~>1.2.x --> >=1.2.0 <1.3.0
+// ~1.2.3, ~>1.2.3 --> >=1.2.3 <1.3.0
+// ~1.2.0, ~>1.2.0 --> >=1.2.0 <1.3.0
+function replaceTildes (comp, options) {
+  return comp.trim().split(/\s+/).map(function (comp) {
+    return replaceTilde(comp, options)
+  }).join(' ')
+}
+
+function replaceTilde (comp, options) {
+  var r = options.loose ? re[TILDELOOSE] : re[TILDE]
+  return comp.replace(r, function (_, M, m, p, pr) {
+    debug('tilde', comp, _, M, m, p, pr)
+    var ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'
+    } else if (isX(p)) {
+      // ~1.2 == >=1.2.0 <1.3.0
+      ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'
+    } else if (pr) {
+      debug('replaceTilde pr', pr)
+      ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+            ' <' + M + '.' + (+m + 1) + '.0'
+    } else {
+      // ~1.2.3 == >=1.2.3 <1.3.0
+      ret = '>=' + M + '.' + m + '.' + p +
+            ' <' + M + '.' + (+m + 1) + '.0'
+    }
+
+    debug('tilde return', ret)
+    return ret
+  })
+}
+
+// ^ --> * (any, kinda silly)
+// ^2, ^2.x, ^2.x.x --> >=2.0.0 <3.0.0
+// ^2.0, ^2.0.x --> >=2.0.0 <3.0.0
+// ^1.2, ^1.2.x --> >=1.2.0 <2.0.0
+// ^1.2.3 --> >=1.2.3 <2.0.0
+// ^1.2.0 --> >=1.2.0 <2.0.0
+function replaceCarets (comp, options) {
+  return comp.trim().split(/\s+/).map(function (comp) {
+    return replaceCaret(comp, options)
+  }).join(' ')
+}
+
+function replaceCaret (comp, options) {
+  debug('caret', comp, options)
+  var r = options.loose ? re[CARETLOOSE] : re[CARET]
+  return comp.replace(r, function (_, M, m, p, pr) {
+    debug('caret', comp, _, M, m, p, pr)
+    var ret
+
+    if (isX(M)) {
+      ret = ''
+    } else if (isX(m)) {
+      ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'
+    } else if (isX(p)) {
+      if (M === '0') {
+        ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'
+      } else {
+        ret = '>=' + M + '.' + m + '.0 <' + (+M + 1) + '.0.0'
+      }
+    } else if (pr) {
+      debug('replaceCaret pr', pr)
+      if (M === '0') {
+        if (m === '0') {
+          ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+                ' <' + M + '.' + m + '.' + (+p + 1)
+        } else {
+          ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+                ' <' + M + '.' + (+m + 1) + '.0'
+        }
+      } else {
+        ret = '>=' + M + '.' + m + '.' + p + '-' + pr +
+              ' <' + (+M + 1) + '.0.0'
+      }
+    } else {
+      debug('no pr')
+      if (M === '0') {
+        if (m === '0') {
+          ret = '>=' + M + '.' + m + '.' + p +
+                ' <' + M + '.' + m + '.' + (+p + 1)
+        } else {
+          ret = '>=' + M + '.' + m + '.' + p +
+                ' <' + M + '.' + (+m + 1) + '.0'
+        }
+      } else {
+        ret = '>=' + M + '.' + m + '.' + p +
+              ' <' + (+M + 1) + '.0.0'
+      }
+    }
+
+    debug('caret return', ret)
+    return ret
+  })
+}
+
+function replaceXRanges (comp, options) {
+  debug('replaceXRanges', comp, options)
+  return comp.split(/\s+/).map(function (comp) {
+    return replaceXRange(comp, options)
+  }).join(' ')
+}
+
+function replaceXRange (comp, options) {
+  comp = comp.trim()
+  var r = options.loose ? re[XRANGELOOSE] : re[XRANGE]
+  return comp.replace(r, function (ret, gtlt, M, m, p, pr) {
+    debug('xRange', comp, ret, gtlt, M, m, p, pr)
+    var xM = isX(M)
+    var xm = xM || isX(m)
+    var xp = xm || isX(p)
+    var anyX = xp
+
+    if (gtlt === '=' && anyX) {
+      gtlt = ''
+    }
+
+    if (xM) {
+      if (gtlt === '>' || gtlt === '<') {
+        // nothing is allowed
+        ret = '<0.0.0'
+      } else {
+        // nothing is forbidden
+        ret = '*'
+      }
+    } else if (gtlt && anyX) {
+      // we know patch is an x, because we have any x at all.
+      // replace X with 0
+      if (xm) {
+        m = 0
+      }
+      p = 0
+
+      if (gtlt === '>') {
+        // >1 => >=2.0.0
+        // >1.2 => >=1.3.0
+        // >1.2.3 => >= 1.2.4
+        gtlt = '>='
+        if (xm) {
+          M = +M + 1
+          m = 0
+          p = 0
+        } else {
+          m = +m + 1
+          p = 0
+        }
+      } else if (gtlt === '<=') {
+        // <=0.7.x is actually <0.8.0, since any 0.7.x should
+        // pass.  Similarly, <=7.x is actually <8.0.0, etc.
+        gtlt = '<'
+        if (xm) {
+          M = +M + 1
+        } else {
+          m = +m + 1
+        }
+      }
+
+      ret = gtlt + M + '.' + m + '.' + p
+    } else if (xm) {
+      ret = '>=' + M + '.0.0 <' + (+M + 1) + '.0.0'
+    } else if (xp) {
+      ret = '>=' + M + '.' + m + '.0 <' + M + '.' + (+m + 1) + '.0'
+    }
+
+    debug('xRange return', ret)
+
+    return ret
+  })
+}
+
+// Because * is AND-ed with everything else in the comparator,
+// and '' means "any version", just remove the *s entirely.
+function replaceStars (comp, options) {
+  debug('replaceStars', comp, options)
+  // Looseness is ignored here.  star is always as loose as it gets!
+  return comp.trim().replace(re[STAR], '')
+}
+
+// This function is passed to string.replace(re[HYPHENRANGE])
+// M, m, patch, prerelease, build
+// 1.2 - 3.4.5 => >=1.2.0 <=3.4.5
+// 1.2.3 - 3.4 => >=1.2.0 <3.5.0 Any 3.4.x will do
+// 1.2 - 3.4 => >=1.2.0 <3.5.0
+function hyphenReplace ($0,
+  from, fM, fm, fp, fpr, fb,
+  to, tM, tm, tp, tpr, tb) {
+  if (isX(fM)) {
+    from = ''
+  } else if (isX(fm)) {
+    from = '>=' + fM + '.0.0'
+  } else if (isX(fp)) {
+    from = '>=' + fM + '.' + fm + '.0'
+  } else {
+    from = '>=' + from
+  }
+
+  if (isX(tM)) {
+    to = ''
+  } else if (isX(tm)) {
+    to = '<' + (+tM + 1) + '.0.0'
+  } else if (isX(tp)) {
+    to = '<' + tM + '.' + (+tm + 1) + '.0'
+  } else if (tpr) {
+    to = '<=' + tM + '.' + tm + '.' + tp + '-' + tpr
+  } else {
+    to = '<=' + to
+  }
+
+  return (from + ' ' + to).trim()
+}
+
+// if ANY of the sets match ALL of its comparators, then pass
+Range.prototype.test = function (version) {
+  if (!version) {
+    return false
+  }
+
+  if (typeof version === 'string') {
+    version = new SemVer(version, this.options)
+  }
+
+  for (var i = 0; i < this.set.length; i++) {
+    if (testSet(this.set[i], version, this.options)) {
+      return true
+    }
+  }
+  return false
+}
+
+function testSet (set, version, options) {
+  for (var i = 0; i < set.length; i++) {
+    if (!set[i].test(version)) {
+      return false
+    }
+  }
+
+  if (version.prerelease.length && !options.includePrerelease) {
+    // Find the set of versions that are allowed to have prereleases
+    // For example, ^1.2.3-pr.1 desugars to >=1.2.3-pr.1 <2.0.0
+    // That should allow `1.2.3-pr.2` to pass.
+    // However, `1.2.4-alpha.notready` should NOT be allowed,
+    // even though it's within the range set by the comparators.
+    for (i = 0; i < set.length; i++) {
+      debug(set[i].semver)
+      if (set[i].semver === ANY) {
+        continue
+      }
+
+      if (set[i].semver.prerelease.length > 0) {
+        var allowed = set[i].semver
+        if (allowed.major === version.major &&
+            allowed.minor === version.minor &&
+            allowed.patch === version.patch) {
+          return true
+        }
+      }
+    }
+
+    // Version has a -pre, but it's not one of the ones we like.
+    return false
+  }
+
+  return true
+}
+
+exports.satisfies = satisfies
+function satisfies (version, range, options) {
+  try {
+    range = new Range(range, options)
+  } catch (er) {
+    return false
+  }
+  return range.test(version)
+}
+
+exports.maxSatisfying = maxSatisfying
+function maxSatisfying (versions, range, options) {
+  var max = null
+  var maxSV = null
+  try {
+    var rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach(function (v) {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!max || maxSV.compare(v) === -1) {
+        // compare(max, v, true)
+        max = v
+        maxSV = new SemVer(max, options)
+      }
+    }
+  })
+  return max
+}
+
+exports.minSatisfying = minSatisfying
+function minSatisfying (versions, range, options) {
+  var min = null
+  var minSV = null
+  try {
+    var rangeObj = new Range(range, options)
+  } catch (er) {
+    return null
+  }
+  versions.forEach(function (v) {
+    if (rangeObj.test(v)) {
+      // satisfies(v, range, options)
+      if (!min || minSV.compare(v) === 1) {
+        // compare(min, v, true)
+        min = v
+        minSV = new SemVer(min, options)
+      }
+    }
+  })
+  return min
+}
+
+exports.minVersion = minVersion
+function minVersion (range, loose) {
+  range = new Range(range, loose)
+
+  var minver = new SemVer('0.0.0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = new SemVer('0.0.0-0')
+  if (range.test(minver)) {
+    return minver
+  }
+
+  minver = null
+  for (var i = 0; i < range.set.length; ++i) {
+    var comparators = range.set[i]
+
+    comparators.forEach(function (comparator) {
+      // Clone to avoid manipulating the comparator's semver object.
+      var compver = new SemVer(comparator.semver.version)
+      switch (comparator.operator) {
+        case '>':
+          if (compver.prerelease.length === 0) {
+            compver.patch++
+          } else {
+            compver.prerelease.push(0)
+          }
+          compver.raw = compver.format()
+          /* fallthrough */
+        case '':
+        case '>=':
+          if (!minver || gt(minver, compver)) {
+            minver = compver
+          }
+          break
+        case '<':
+        case '<=':
+          /* Ignore maximum versions */
+          break
+        /* istanbul ignore next */
+        default:
+          throw new Error('Unexpected operation: ' + comparator.operator)
+      }
+    })
+  }
+
+  if (minver && range.test(minver)) {
+    return minver
+  }
+
+  return null
+}
+
+exports.validRange = validRange
+function validRange (range, options) {
+  try {
+    // Return '*' instead of '' so that truthiness works.
+    // This will throw if it's invalid anyway
+    return new Range(range, options).range || '*'
+  } catch (er) {
+    return null
+  }
+}
+
+// Determine if version is less than all the versions possible in the range
+exports.ltr = ltr
+function ltr (version, range, options) {
+  return outside(version, range, '<', options)
+}
+
+// Determine if version is greater than all the versions possible in the range.
+exports.gtr = gtr
+function gtr (version, range, options) {
+  return outside(version, range, '>', options)
+}
+
+exports.outside = outside
+function outside (version, range, hilo, options) {
+  version = new SemVer(version, options)
+  range = new Range(range, options)
+
+  var gtfn, ltefn, ltfn, comp, ecomp
+  switch (hilo) {
+    case '>':
+      gtfn = gt
+      ltefn = lte
+      ltfn = lt
+      comp = '>'
+      ecomp = '>='
+      break
+    case '<':
+      gtfn = lt
+      ltefn = gte
+      ltfn = gt
+      comp = '<'
+      ecomp = '<='
+      break
+    default:
+      throw new TypeError('Must provide a hilo val of "<" or ">"')
+  }
+
+  // If it satisifes the range it is not outside
+  if (satisfies(version, range, options)) {
+    return false
+  }
+
+  // From now on, variable terms are as if we're in "gtr" mode.
+  // but note that everything is flipped for the "ltr" function.
+
+  for (var i = 0; i < range.set.length; ++i) {
+    var comparators = range.set[i]
+
+    var high = null
+    var low = null
+
+    comparators.forEach(function (comparator) {
+      if (comparator.semver === ANY) {
+        comparator = new Comparator('>=0.0.0')
+      }
+      high = high || comparator
+      low = low || comparator
+      if (gtfn(comparator.semver, high.semver, options)) {
+        high = comparator
+      } else if (ltfn(comparator.semver, low.semver, options)) {
+        low = comparator
+      }
+    })
+
+    // If the edge version comparator has a operator then our version
+    // isn't outside it
+    if (high.operator === comp || high.operator === ecomp) {
+      return false
+    }
+
+    // If the lowest version comparator has an operator and our version
+    // is less than it then it isn't higher than the range
+    if ((!low.operator || low.operator === comp) &&
+        ltefn(version, low.semver)) {
+      return false
+    } else if (low.operator === ecomp && ltfn(version, low.semver)) {
+      return false
+    }
+  }
+  return true
+}
+
+exports.prerelease = prerelease
+function prerelease (version, options) {
+  var parsed = parse(version, options)
+  return (parsed && parsed.prerelease.length) ? parsed.prerelease : null
+}
+
+exports.intersects = intersects
+function intersects (r1, r2, options) {
+  r1 = new Range(r1, options)
+  r2 = new Range(r2, options)
+  return r1.intersects(r2)
+}
+
+exports.coerce = coerce
+function coerce (version) {
+  if (version instanceof SemVer) {
+    return version
+  }
+
+  if (typeof version !== 'string') {
+    return null
+  }
+
+  var match = version.match(re[COERCE])
+
+  if (match == null) {
+    return null
+  }
+
+  return parse(match[1] +
+    '.' + (match[2] || '0') +
+    '.' + (match[3] || '0'))
+}
+
+
+/***/ }),
+
+/***/ 471:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticationBeforeRequest;
+
+const btoa = __webpack_require__(675);
+const uniq = __webpack_require__(126);
+
+function authenticationBeforeRequest(state, options) {
+  if (!state.auth.type) {
+    return;
+  }
+
+  if (state.auth.type === "basic") {
+    const hash = btoa(`${state.auth.username}:${state.auth.password}`);
+    options.headers.authorization = `Basic ${hash}`;
+    return;
+  }
+
+  if (state.auth.type === "token") {
+    options.headers.authorization = `token ${state.auth.token}`;
+    return;
+  }
+
+  if (state.auth.type === "app") {
+    options.headers.authorization = `Bearer ${state.auth.token}`;
+    const acceptHeaders = options.headers.accept
+      .split(",")
+      .concat("application/vnd.github.machine-man-preview+json");
+    options.headers.accept = uniq(acceptHeaders)
+      .filter(Boolean)
+      .join(",");
+    return;
+  }
+
+  options.url += options.url.indexOf("?") === -1 ? "?" : "&";
+
+  if (state.auth.token) {
+    options.url += `access_token=${encodeURIComponent(state.auth.token)}`;
+    return;
+  }
+
+  const key = encodeURIComponent(state.auth.key);
+  const secret = encodeURIComponent(state.auth.secret);
+  options.url += `client_id=${key}&client_secret=${secret}`;
+}
+
+
+/***/ }),
+
+/***/ 489:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+
+const path = __webpack_require__(622);
+const which = __webpack_require__(814);
+const pathKey = __webpack_require__(39)();
+
+function resolveCommandAttempt(parsed, withoutPathExt) {
+    const cwd = process.cwd();
+    const hasCustomCwd = parsed.options.cwd != null;
+
+    // If a custom `cwd` was specified, we need to change the process cwd
+    // because `which` will do stat calls but does not support a custom cwd
+    if (hasCustomCwd) {
+        try {
+            process.chdir(parsed.options.cwd);
+        } catch (err) {
+            /* Empty */
+        }
+    }
+
+    let resolved;
+
+    try {
+        resolved = which.sync(parsed.command, {
+            path: (parsed.options.env || process.env)[pathKey],
+            pathExt: withoutPathExt ? path.delimiter : undefined,
+        });
+    } catch (e) {
+        /* Empty */
+    } finally {
+        process.chdir(cwd);
+    }
+
+    // If we successfully resolved, ensure that an absolute path is returned
+    // Note that when a custom `cwd` was used, we need to resolve to an absolute path based on it
+    if (resolved) {
+        resolved = path.resolve(hasCustomCwd ? parsed.options.cwd : '', resolved);
+    }
+
+    return resolved;
+}
+
+function resolveCommand(parsed) {
+    return resolveCommandAttempt(parsed) || resolveCommandAttempt(parsed, true);
+}
+
+module.exports = resolveCommand;
+
+
+/***/ }),
+
+/***/ 510:
+/***/ (function(module) {
+
+module.exports = addHook
+
+function addHook (state, kind, name, hook) {
+  var orig = hook
+  if (!state.registry[name]) {
+    state.registry[name] = []
+  }
+
+  if (kind === 'before') {
+    hook = function (method, options) {
+      return Promise.resolve()
+        .then(orig.bind(null, options))
+        .then(method.bind(null, options))
+    }
+  }
+
+  if (kind === 'after') {
+    hook = function (method, options) {
+      var result
+      return Promise.resolve()
+        .then(method.bind(null, options))
+        .then(function (result_) {
+          result = result_
+          return orig(result, options)
+        })
+        .then(function () {
+          return result
+        })
+    }
+  }
+
+  if (kind === 'error') {
+    hook = function (method, options) {
+      return Promise.resolve()
+        .then(method.bind(null, options))
+        .catch(function (error) {
+          return orig(error, options)
+        })
+    }
+  }
+
+  state.registry[name].push({
+    hook: hook,
+    orig: orig
+  })
+}
+
+
+/***/ }),
+
+/***/ 523:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+var register = __webpack_require__(280)
+var addHook = __webpack_require__(510)
+var removeHook = __webpack_require__(763)
+
+// bind with array of arguments: https://stackoverflow.com/a/21792913
+var bind = Function.bind
+var bindable = bind.bind(bind)
+
+function bindApi (hook, state, name) {
+  var removeHookRef = bindable(removeHook, null).apply(null, name ? [state, name] : [state])
+  hook.api = { remove: removeHookRef }
+  hook.remove = removeHookRef
+
+  ;['before', 'error', 'after', 'wrap'].forEach(function (kind) {
+    var args = name ? [state, kind, name] : [state, kind]
+    hook[kind] = hook.api[kind] = bindable(addHook, null).apply(null, args)
+  })
+}
+
+function HookSingular () {
+  var singularHookName = 'h'
+  var singularHookState = {
+    registry: {}
+  }
+  var singularHook = register.bind(null, singularHookState, singularHookName)
+  bindApi(singularHook, singularHookState, singularHookName)
+  return singularHook
+}
+
+function HookCollection () {
+  var state = {
+    registry: {}
+  }
+
+  var hook = register.bind(null, state)
+  bindApi(hook, state)
+
+  return hook
+}
+
+var collectionHookDeprecationMessageDisplayed = false
+function Hook () {
+  if (!collectionHookDeprecationMessageDisplayed) {
+    console.warn('[before-after-hook]: "Hook()" repurposing warning, use "Hook.Collection()". Read more: https://git.io/upgrade-before-after-hook-to-1.4')
+    collectionHookDeprecationMessageDisplayed = true
+  }
+  return HookCollection()
+}
+
+Hook.Singular = HookSingular.bind()
+Hook.Collection = HookCollection.bind()
+
+module.exports = Hook
+// expose constructors as a named property for TypeScript
+module.exports.Hook = Hook
+module.exports.Singular = Hook.Singular
+module.exports.Collection = Hook.Collection
+
+
+/***/ }),
+
+/***/ 529:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+const factory = __webpack_require__(47);
+
+module.exports = factory();
+
+
+/***/ }),
+
+/***/ 536:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = hasFirstPage
+
+const deprecate = __webpack_require__(370)
+const getPageLinks = __webpack_require__(577)
+
+function hasFirstPage (link) {
+  deprecate(`octokit.hasFirstPage() – You can use octokit.paginate or async iterators instead: https://github.com/octokit/rest.js#pagination.`)
+  return getPageLinks(link).first
+}
+
+
+/***/ }),
+
+/***/ 539:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+Object.defineProperty(exports, "__esModule", { value: true });
+const url = __webpack_require__(835);
+const http = __webpack_require__(605);
+const https = __webpack_require__(211);
+const pm = __webpack_require__(950);
+let tunnel;
+var HttpCodes;
+(function (HttpCodes) {
+    HttpCodes[HttpCodes["OK"] = 200] = "OK";
+    HttpCodes[HttpCodes["MultipleChoices"] = 300] = "MultipleChoices";
+    HttpCodes[HttpCodes["MovedPermanently"] = 301] = "MovedPermanently";
+    HttpCodes[HttpCodes["ResourceMoved"] = 302] = "ResourceMoved";
+    HttpCodes[HttpCodes["SeeOther"] = 303] = "SeeOther";
+    HttpCodes[HttpCodes["NotModified"] = 304] = "NotModified";
+    HttpCodes[HttpCodes["UseProxy"] = 305] = "UseProxy";
+    HttpCodes[HttpCodes["SwitchProxy"] = 306] = "SwitchProxy";
+    HttpCodes[HttpCodes["TemporaryRedirect"] = 307] = "TemporaryRedirect";
+    HttpCodes[HttpCodes["PermanentRedirect"] = 308] = "PermanentRedirect";
+    HttpCodes[HttpCodes["BadRequest"] = 400] = "BadRequest";
+    HttpCodes[HttpCodes["Unauthorized"] = 401] = "Unauthorized";
+    HttpCodes[HttpCodes["PaymentRequired"] = 402] = "PaymentRequired";
+    HttpCodes[HttpCodes["Forbidden"] = 403] = "Forbidden";
+    HttpCodes[HttpCodes["NotFound"] = 404] = "NotFound";
+    HttpCodes[HttpCodes["MethodNotAllowed"] = 405] = "MethodNotAllowed";
+    HttpCodes[HttpCodes["NotAcceptable"] = 406] = "NotAcceptable";
+    HttpCodes[HttpCodes["ProxyAuthenticationRequired"] = 407] = "ProxyAuthenticationRequired";
+    HttpCodes[HttpCodes["RequestTimeout"] = 408] = "RequestTimeout";
+    HttpCodes[HttpCodes["Conflict"] = 409] = "Conflict";
+    HttpCodes[HttpCodes["Gone"] = 410] = "Gone";
+    HttpCodes[HttpCodes["InternalServerError"] = 500] = "InternalServerError";
+    HttpCodes[HttpCodes["NotImplemented"] = 501] = "NotImplemented";
+    HttpCodes[HttpCodes["BadGateway"] = 502] = "BadGateway";
+    HttpCodes[HttpCodes["ServiceUnavailable"] = 503] = "ServiceUnavailable";
+    HttpCodes[HttpCodes["GatewayTimeout"] = 504] = "GatewayTimeout";
+})(HttpCodes = exports.HttpCodes || (exports.HttpCodes = {}));
+/**
+ * Returns the proxy URL, depending upon the supplied url and proxy environment variables.
+ * @param serverUrl  The server URL where the request will be sent. For example, https://api.github.com
+ */
+function getProxyUrl(serverUrl) {
+    let proxyUrl = pm.getProxyUrl(url.parse(serverUrl));
+    return proxyUrl ? proxyUrl.href : '';
+}
+exports.getProxyUrl = getProxyUrl;
+const HttpRedirectCodes = [HttpCodes.MovedPermanently, HttpCodes.ResourceMoved, HttpCodes.SeeOther, HttpCodes.TemporaryRedirect, HttpCodes.PermanentRedirect];
+const HttpResponseRetryCodes = [HttpCodes.BadGateway, HttpCodes.ServiceUnavailable, HttpCodes.GatewayTimeout];
+const RetryableHttpVerbs = ['OPTIONS', 'GET', 'DELETE', 'HEAD'];
+const ExponentialBackoffCeiling = 10;
+const ExponentialBackoffTimeSlice = 5;
+class HttpClientResponse {
+    constructor(message) {
+        this.message = message;
+    }
+    readBody() {
+        return new Promise(async (resolve, reject) => {
+            let output = Buffer.alloc(0);
+            this.message.on('data', (chunk) => {
+                output = Buffer.concat([output, chunk]);
+            });
+            this.message.on('end', () => {
+                resolve(output.toString());
+            });
+        });
+    }
+}
+exports.HttpClientResponse = HttpClientResponse;
+function isHttps(requestUrl) {
+    let parsedUrl = url.parse(requestUrl);
+    return parsedUrl.protocol === 'https:';
+}
+exports.isHttps = isHttps;
+class HttpClient {
+    constructor(userAgent, handlers, requestOptions) {
+        this._ignoreSslError = false;
+        this._allowRedirects = true;
+        this._allowRedirectDowngrade = false;
+        this._maxRedirects = 50;
+        this._allowRetries = false;
+        this._maxRetries = 1;
+        this._keepAlive = false;
+        this._disposed = false;
+        this.userAgent = userAgent;
+        this.handlers = handlers || [];
+        this.requestOptions = requestOptions;
+        if (requestOptions) {
+            if (requestOptions.ignoreSslError != null) {
+                this._ignoreSslError = requestOptions.ignoreSslError;
+            }
+            this._socketTimeout = requestOptions.socketTimeout;
+            if (requestOptions.allowRedirects != null) {
+                this._allowRedirects = requestOptions.allowRedirects;
+            }
+            if (requestOptions.allowRedirectDowngrade != null) {
+                this._allowRedirectDowngrade = requestOptions.allowRedirectDowngrade;
+            }
+            if (requestOptions.maxRedirects != null) {
+                this._maxRedirects = Math.max(requestOptions.maxRedirects, 0);
+            }
+            if (requestOptions.keepAlive != null) {
+                this._keepAlive = requestOptions.keepAlive;
+            }
+            if (requestOptions.allowRetries != null) {
+                this._allowRetries = requestOptions.allowRetries;
+            }
+            if (requestOptions.maxRetries != null) {
+                this._maxRetries = requestOptions.maxRetries;
+            }
+        }
+    }
+    options(requestUrl, additionalHeaders) {
+        return this.request('OPTIONS', requestUrl, null, additionalHeaders || {});
+    }
+    get(requestUrl, additionalHeaders) {
+        return this.request('GET', requestUrl, null, additionalHeaders || {});
+    }
+    del(requestUrl, additionalHeaders) {
+        return this.request('DELETE', requestUrl, null, additionalHeaders || {});
+    }
+    post(requestUrl, data, additionalHeaders) {
+        return this.request('POST', requestUrl, data, additionalHeaders || {});
+    }
+    patch(requestUrl, data, additionalHeaders) {
+        return this.request('PATCH', requestUrl, data, additionalHeaders || {});
+    }
+    put(requestUrl, data, additionalHeaders) {
+        return this.request('PUT', requestUrl, data, additionalHeaders || {});
+    }
+    head(requestUrl, additionalHeaders) {
+        return this.request('HEAD', requestUrl, null, additionalHeaders || {});
+    }
+    sendStream(verb, requestUrl, stream, additionalHeaders) {
+        return this.request(verb, requestUrl, stream, additionalHeaders);
+    }
+    /**
+     * Gets a typed object from an endpoint
+     * Be aware that not found returns a null.  Other errors (4xx, 5xx) reject the promise
+     */
+    async getJson(requestUrl, additionalHeaders) {
+        let res = await this.get(requestUrl, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async postJson(requestUrl, obj, additionalHeaders) {
+        let data = JSON.stringify(obj, null, 2);
+        let res = await this.post(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async putJson(requestUrl, obj, additionalHeaders) {
+        let data = JSON.stringify(obj, null, 2);
+        let res = await this.put(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    async patchJson(requestUrl, obj, additionalHeaders) {
+        let data = JSON.stringify(obj, null, 2);
+        let res = await this.patch(requestUrl, data, additionalHeaders);
+        return this._processResponse(res, this.requestOptions);
+    }
+    /**
+     * Makes a raw http request.
+     * All other methods such as get, post, patch, and request ultimately call this.
+     * Prefer get, del, post and patch
+     */
+    async request(verb, requestUrl, data, headers) {
+        if (this._disposed) {
+            throw new Error("Client has already been disposed.");
+        }
+        let parsedUrl = url.parse(requestUrl);
+        let info = this._prepareRequest(verb, parsedUrl, headers);
+        // Only perform retries on reads since writes may not be idempotent.
+        let maxTries = (this._allowRetries && RetryableHttpVerbs.indexOf(verb) != -1) ? this._maxRetries + 1 : 1;
+        let numTries = 0;
+        let response;
+        while (numTries < maxTries) {
+            response = await this.requestRaw(info, data);
+            // Check if it's an authentication challenge
+            if (response && response.message && response.message.statusCode === HttpCodes.Unauthorized) {
+                let authenticationHandler;
+                for (let i = 0; i < this.handlers.length; i++) {
+                    if (this.handlers[i].canHandleAuthentication(response)) {
+                        authenticationHandler = this.handlers[i];
+                        break;
+                    }
+                }
+                if (authenticationHandler) {
+                    return authenticationHandler.handleAuthentication(this, info, data);
+                }
+                else {
+                    // We have received an unauthorized response but have no handlers to handle it.
+                    // Let the response return to the caller.
+                    return response;
+                }
+            }
+            let redirectsRemaining = this._maxRedirects;
+            while (HttpRedirectCodes.indexOf(response.message.statusCode) != -1
+                && this._allowRedirects
+                && redirectsRemaining > 0) {
+                const redirectUrl = response.message.headers["location"];
+                if (!redirectUrl) {
+                    // if there's no location to redirect to, we won't
+                    break;
+                }
+                let parsedRedirectUrl = url.parse(redirectUrl);
+                if (parsedUrl.protocol == 'https:' && parsedUrl.protocol != parsedRedirectUrl.protocol && !this._allowRedirectDowngrade) {
+                    throw new Error("Redirect from HTTPS to HTTP protocol. This downgrade is not allowed for security reasons. If you want to allow this behavior, set the allowRedirectDowngrade option to true.");
+                }
+                // we need to finish reading the response before reassigning response
+                // which will leak the open socket.
+                await response.readBody();
+                // let's make the request with the new redirectUrl
+                info = this._prepareRequest(verb, parsedRedirectUrl, headers);
+                response = await this.requestRaw(info, data);
+                redirectsRemaining--;
+            }
+            if (HttpResponseRetryCodes.indexOf(response.message.statusCode) == -1) {
+                // If not a retry code, return immediately instead of retrying
+                return response;
+            }
+            numTries += 1;
+            if (numTries < maxTries) {
+                await response.readBody();
+                await this._performExponentialBackoff(numTries);
+            }
+        }
+        return response;
+    }
+    /**
+     * Needs to be called if keepAlive is set to true in request options.
+     */
+    dispose() {
+        if (this._agent) {
+            this._agent.destroy();
+        }
+        this._disposed = true;
+    }
+    /**
+     * Raw request.
+     * @param info
+     * @param data
+     */
+    requestRaw(info, data) {
+        return new Promise((resolve, reject) => {
+            let callbackForResult = function (err, res) {
+                if (err) {
+                    reject(err);
+                }
+                resolve(res);
+            };
+            this.requestRawWithCallback(info, data, callbackForResult);
+        });
+    }
+    /**
+     * Raw request with callback.
+     * @param info
+     * @param data
+     * @param onResult
+     */
+    requestRawWithCallback(info, data, onResult) {
+        let socket;
+        if (typeof (data) === 'string') {
+            info.options.headers["Content-Length"] = Buffer.byteLength(data, 'utf8');
+        }
+        let callbackCalled = false;
+        let handleResult = (err, res) => {
+            if (!callbackCalled) {
+                callbackCalled = true;
+                onResult(err, res);
+            }
+        };
+        let req = info.httpModule.request(info.options, (msg) => {
+            let res = new HttpClientResponse(msg);
+            handleResult(null, res);
+        });
+        req.on('socket', (sock) => {
+            socket = sock;
+        });
+        // If we ever get disconnected, we want the socket to timeout eventually
+        req.setTimeout(this._socketTimeout || 3 * 60000, () => {
+            if (socket) {
+                socket.end();
+            }
+            handleResult(new Error('Request timeout: ' + info.options.path), null);
+        });
+        req.on('error', function (err) {
+            // err has statusCode property
+            // res should have headers
+            handleResult(err, null);
+        });
+        if (data && typeof (data) === 'string') {
+            req.write(data, 'utf8');
+        }
+        if (data && typeof (data) !== 'string') {
+            data.on('close', function () {
+                req.end();
+            });
+            data.pipe(req);
+        }
+        else {
+            req.end();
+        }
+    }
+    /**
+     * Gets an http agent. This function is useful when you need an http agent that handles
+     * routing through a proxy server - depending upon the url and proxy environment variables.
+     * @param serverUrl  The server URL where the request will be sent. For example, https://api.github.com
+     */
+    getAgent(serverUrl) {
+        let parsedUrl = url.parse(serverUrl);
+        return this._getAgent(parsedUrl);
+    }
+    _prepareRequest(method, requestUrl, headers) {
+        const info = {};
+        info.parsedUrl = requestUrl;
+        const usingSsl = info.parsedUrl.protocol === 'https:';
+        info.httpModule = usingSsl ? https : http;
+        const defaultPort = usingSsl ? 443 : 80;
+        info.options = {};
+        info.options.host = info.parsedUrl.hostname;
+        info.options.port = info.parsedUrl.port ? parseInt(info.parsedUrl.port) : defaultPort;
+        info.options.path = (info.parsedUrl.pathname || '') + (info.parsedUrl.search || '');
+        info.options.method = method;
+        info.options.headers = this._mergeHeaders(headers);
+        if (this.userAgent != null) {
+            info.options.headers["user-agent"] = this.userAgent;
+        }
+        info.options.agent = this._getAgent(info.parsedUrl);
+        // gives handlers an opportunity to participate
+        if (this.handlers) {
+            this.handlers.forEach((handler) => {
+                handler.prepareRequest(info.options);
+            });
+        }
+        return info;
+    }
+    _mergeHeaders(headers) {
+        const lowercaseKeys = obj => Object.keys(obj).reduce((c, k) => (c[k.toLowerCase()] = obj[k], c), {});
+        if (this.requestOptions && this.requestOptions.headers) {
+            return Object.assign({}, lowercaseKeys(this.requestOptions.headers), lowercaseKeys(headers));
+        }
+        return lowercaseKeys(headers || {});
+    }
+    _getAgent(parsedUrl) {
+        let agent;
+        let proxyUrl = pm.getProxyUrl(parsedUrl);
+        let useProxy = proxyUrl && proxyUrl.hostname;
+        if (this._keepAlive && useProxy) {
+            agent = this._proxyAgent;
+        }
+        if (this._keepAlive && !useProxy) {
+            agent = this._agent;
+        }
+        // if agent is already assigned use that agent.
+        if (!!agent) {
+            return agent;
+        }
+        const usingSsl = parsedUrl.protocol === 'https:';
+        let maxSockets = 100;
+        if (!!this.requestOptions) {
+            maxSockets = this.requestOptions.maxSockets || http.globalAgent.maxSockets;
+        }
+        if (useProxy) {
+            // If using proxy, need tunnel
+            if (!tunnel) {
+                tunnel = __webpack_require__(856);
+            }
+            const agentOptions = {
+                maxSockets: maxSockets,
+                keepAlive: this._keepAlive,
+                proxy: {
+                    proxyAuth: proxyUrl.auth,
+                    host: proxyUrl.hostname,
+                    port: proxyUrl.port
+                },
+            };
+            let tunnelAgent;
+            const overHttps = proxyUrl.protocol === 'https:';
+            if (usingSsl) {
+                tunnelAgent = overHttps ? tunnel.httpsOverHttps : tunnel.httpsOverHttp;
+            }
+            else {
+                tunnelAgent = overHttps ? tunnel.httpOverHttps : tunnel.httpOverHttp;
+            }
+            agent = tunnelAgent(agentOptions);
+            this._proxyAgent = agent;
+        }
+        // if reusing agent across request and tunneling agent isn't assigned create a new agent
+        if (this._keepAlive && !agent) {
+            const options = { keepAlive: this._keepAlive, maxSockets: maxSockets };
+            agent = usingSsl ? new https.Agent(options) : new http.Agent(options);
+            this._agent = agent;
+        }
+        // if not using private agent and tunnel agent isn't setup then use global agent
+        if (!agent) {
+            agent = usingSsl ? https.globalAgent : http.globalAgent;
+        }
+        if (usingSsl && this._ignoreSslError) {
+            // we don't want to set NODE_TLS_REJECT_UNAUTHORIZED=0 since that will affect request for entire process
+            // http.RequestOptions doesn't expose a way to modify RequestOptions.agent.options
+            // we have to cast it to any and change it directly
+            agent.options = Object.assign(agent.options || {}, { rejectUnauthorized: false });
+        }
+        return agent;
+    }
+    _performExponentialBackoff(retryNumber) {
+        retryNumber = Math.min(ExponentialBackoffCeiling, retryNumber);
+        const ms = ExponentialBackoffTimeSlice * Math.pow(2, retryNumber);
+        return new Promise(resolve => setTimeout(() => resolve(), ms));
+    }
+    static dateTimeDeserializer(key, value) {
+        if (typeof value === 'string') {
+            let a = new Date(value);
+            if (!isNaN(a.valueOf())) {
+                return a;
+            }
+        }
+        return value;
+    }
+    async _processResponse(res, options) {
+        return new Promise(async (resolve, reject) => {
+            const statusCode = res.message.statusCode;
+            const response = {
+                statusCode: statusCode,
+                result: null,
+                headers: {}
+            };
+            // not found leads to null obj returned
+            if (statusCode == HttpCodes.NotFound) {
+                resolve(response);
+            }
+            let obj;
+            let contents;
+            // get the result from the body
+            try {
+                contents = await res.readBody();
+                if (contents && contents.length > 0) {
+                    if (options && options.deserializeDates) {
+                        obj = JSON.parse(contents, HttpClient.dateTimeDeserializer);
+                    }
+                    else {
+                        obj = JSON.parse(contents);
+                    }
+                    response.result = obj;
+                }
+                response.headers = res.message.headers;
+            }
+            catch (err) {
+                // Invalid resource (contents not json);  leaving result obj null
+            }
+            // note that 3xx redirects are handled by the http layer.
+            if (statusCode > 299) {
+                let msg;
+                // if exception/error in body, attempt to get better error
+                if (obj && obj.message) {
+                    msg = obj.message;
+                }
+                else if (contents && contents.length > 0) {
+                    // it may be the case that the exception is in the body message as string
+                    msg = contents;
+                }
+                else {
+                    msg = "Failed request: (" + statusCode + ")";
+                }
+                let err = new Error(msg);
+                // attach statusCode and body obj (if available) to the error object
+                err['statusCode'] = statusCode;
+                if (response.result) {
+                    err['result'] = response.result;
+                }
+                reject(err);
+            }
+            else {
+                resolve(response);
+            }
+        });
+    }
+}
+exports.HttpClient = HttpClient;
+
+
+/***/ }),
+
+/***/ 548:
+/***/ (function(module) {
+
+"use strict";
+
+
+/*!
+ * isobject <https://github.com/jonschlinkert/isobject>
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+function isObject(val) {
+  return val != null && typeof val === 'object' && Array.isArray(val) === false;
+}
+
+/*!
+ * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+function isObjectObject(o) {
+  return isObject(o) === true
+    && Object.prototype.toString.call(o) === '[object Object]';
+}
+
+function isPlainObject(o) {
+  var ctor,prot;
+
+  if (isObjectObject(o) === false) return false;
+
+  // If has modified constructor
+  ctor = o.constructor;
+  if (typeof ctor !== 'function') return false;
+
+  // If has modified prototype
+  prot = ctor.prototype;
+  if (isObjectObject(prot) === false) return false;
+
+  // If constructor does not have an Object-specific method
+  if (prot.hasOwnProperty('isPrototypeOf') === false) {
+    return false;
+  }
+
+  // Most likely a plain Object
+  return true;
+}
+
+module.exports = isPlainObject;
+
+
+/***/ }),
+
+/***/ 550:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = getNextPage
+
+const getPage = __webpack_require__(265)
+
+function getNextPage (octokit, link, headers) {
+  return getPage(octokit, link, 'next', headers)
+}
+
+
+/***/ }),
+
+/***/ 558:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = hasPreviousPage
+
+const deprecate = __webpack_require__(370)
+const getPageLinks = __webpack_require__(577)
+
+function hasPreviousPage (link) {
+  deprecate(`octokit.hasPreviousPage() – You can use octokit.paginate or async iterators instead: https://github.com/octokit/rest.js#pagination.`)
+  return getPageLinks(link).prev
+}
+
+
+/***/ }),
+
+/***/ 563:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = getPreviousPage
+
+const getPage = __webpack_require__(265)
+
+function getPreviousPage (octokit, link, headers) {
+  return getPage(octokit, link, 'prev', headers)
+}
+
+
+/***/ }),
+
+/***/ 568:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+
+const path = __webpack_require__(622);
+const niceTry = __webpack_require__(948);
+const resolveCommand = __webpack_require__(489);
+const escape = __webpack_require__(462);
+const readShebang = __webpack_require__(389);
+const semver = __webpack_require__(470);
+
+const isWin = process.platform === 'win32';
+const isExecutableRegExp = /\.(?:com|exe)$/i;
+const isCmdShimRegExp = /node_modules[\\/].bin[\\/][^\\/]+\.cmd$/i;
+
+// `options.shell` is supported in Node ^4.8.0, ^5.7.0 and >= 6.0.0
+const supportsShellOption = niceTry(() => semver.satisfies(process.version, '^4.8.0 || ^5.7.0 || >= 6.0.0', true)) || false;
+
+function detectShebang(parsed) {
+    parsed.file = resolveCommand(parsed);
+
+    const shebang = parsed.file && readShebang(parsed.file);
+
+    if (shebang) {
+        parsed.args.unshift(parsed.file);
+        parsed.command = shebang;
+
+        return resolveCommand(parsed);
+    }
+
+    return parsed.file;
+}
+
+function parseNonShell(parsed) {
+    if (!isWin) {
+        return parsed;
+    }
+
+    // Detect & add support for shebangs
+    const commandFile = detectShebang(parsed);
+
+    // We don't need a shell if the command filename is an executable
+    const needsShell = !isExecutableRegExp.test(commandFile);
+
+    // If a shell is required, use cmd.exe and take care of escaping everything correctly
+    // Note that `forceShell` is an hidden option used only in tests
+    if (parsed.options.forceShell || needsShell) {
+        // Need to double escape meta chars if the command is a cmd-shim located in `node_modules/.bin/`
+        // The cmd-shim simply calls execute the package bin file with NodeJS, proxying any argument
+        // Because the escape of metachars with ^ gets interpreted when the cmd.exe is first called,
+        // we need to double escape them
+        const needsDoubleEscapeMetaChars = isCmdShimRegExp.test(commandFile);
+
+        // Normalize posix paths into OS compatible paths (e.g.: foo/bar -> foo\bar)
+        // This is necessary otherwise it will always fail with ENOENT in those cases
+        parsed.command = path.normalize(parsed.command);
+
+        // Escape command & arguments
+        parsed.command = escape.command(parsed.command);
+        parsed.args = parsed.args.map((arg) => escape.argument(arg, needsDoubleEscapeMetaChars));
+
+        const shellCommand = [parsed.command].concat(parsed.args).join(' ');
+
+        parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
+        parsed.command = process.env.comspec || 'cmd.exe';
+        parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
+    }
+
+    return parsed;
+}
+
+function parseShell(parsed) {
+    // If node supports the shell option, there's no need to mimic its behavior
+    if (supportsShellOption) {
+        return parsed;
+    }
+
+    // Mimic node shell option
+    // See https://github.com/nodejs/node/blob/b9f6a2dc059a1062776133f3d4fd848c4da7d150/lib/child_process.js#L335
+    const shellCommand = [parsed.command].concat(parsed.args).join(' ');
+
+    if (isWin) {
+        parsed.command = typeof parsed.options.shell === 'string' ? parsed.options.shell : process.env.comspec || 'cmd.exe';
+        parsed.args = ['/d', '/s', '/c', `"${shellCommand}"`];
+        parsed.options.windowsVerbatimArguments = true; // Tell node's spawn that the arguments are already escaped
+    } else {
+        if (typeof parsed.options.shell === 'string') {
+            parsed.command = parsed.options.shell;
+        } else if (process.platform === 'android') {
+            parsed.command = '/system/bin/sh';
+        } else {
+            parsed.command = '/bin/sh';
+        }
+
+        parsed.args = ['-c', shellCommand];
+    }
+
+    return parsed;
+}
+
+function parse(command, args, options) {
+    // Normalize arguments, similar to nodejs
+    if (args && !Array.isArray(args)) {
+        options = args;
+        args = null;
+    }
+
+    args = args ? args.slice(0) : []; // Clone array to avoid changing the original
+    options = Object.assign({}, options); // Clone object to avoid changing the original
+
+    // Build our parsed object
+    const parsed = {
+        command,
+        args,
+        options,
+        file: undefined,
+        original: {
+            command,
+            args,
+        },
+    };
+
+    // Delegate further parsing to shell or non-shell
+    return options.shell ? parseShell(parsed) : parseNonShell(parsed);
+}
+
+module.exports = parse;
+
+
+/***/ }),
+
+/***/ 577:
+/***/ (function(module) {
+
+module.exports = getPageLinks
+
+function getPageLinks (link) {
+  link = link.link || link.headers.link || ''
+
+  const links = {}
+
+  // link format:
+  // '<https://api.github.com/users/aseemk/followers?page=2>; rel="next", <https://api.github.com/users/aseemk/followers?page=2>; rel="last"'
+  link.replace(/<([^>]*)>;\s*rel="([\w]*)"/g, (m, uri, type) => {
+    links[type] = uri
+  })
+
+  return links
+}
+
+
+/***/ }),
+
+/***/ 605:
+/***/ (function(module) {
+
+module.exports = require("http");
+
+/***/ }),
+
+/***/ 614:
+/***/ (function(module) {
+
+module.exports = require("events");
+
+/***/ }),
+
+/***/ 621:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+"use strict";
+
+const path = __webpack_require__(622);
+const pathKey = __webpack_require__(39);
+
+module.exports = opts => {
+	opts = Object.assign({
+		cwd: process.cwd(),
+		path: process.env[pathKey()]
+	}, opts);
+
+	let prev;
+	let pth = path.resolve(opts.cwd);
+	const ret = [];
+
+	while (prev !== pth) {
+		ret.push(path.join(pth, 'node_modules/.bin'));
+		prev = pth;
+		pth = path.resolve(pth, '..');
+	}
+
+	// ensure the running `node` binary is used
+	ret.push(path.dirname(process.execPath));
+
+	return ret.concat(opts.path).join(path.delimiter);
+};
+
+module.exports.env = opts => {
+	opts = Object.assign({
+		env: process.env
+	}, opts);
+
+	const env = Object.assign({}, opts.env);
+	const path = pathKey({env});
+
+	opts.path = env[path];
+	env[path] = module.exports(opts);
+
+	return env;
+};
+
+
+/***/ }),
+
+/***/ 622:
+/***/ (function(module) {
+
+module.exports = require("path");
+
+/***/ }),
+
+/***/ 626:
+/***/ (function(module) {
+
+"use strict";
+
+
+/*!
+ * isobject <https://github.com/jonschlinkert/isobject>
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+function isObject(val) {
+  return val != null && typeof val === 'object' && Array.isArray(val) === false;
+}
+
+/*!
+ * is-plain-object <https://github.com/jonschlinkert/is-plain-object>
+ *
+ * Copyright (c) 2014-2017, Jon Schlinkert.
+ * Released under the MIT License.
+ */
+
+function isObjectObject(o) {
+  return isObject(o) === true
+    && Object.prototype.toString.call(o) === '[object Object]';
+}
+
+function isPlainObject(o) {
+  var ctor,prot;
+
+  if (isObjectObject(o) === false) return false;
+
+  // If has modified constructor
+  ctor = o.constructor;
+  if (typeof ctor !== 'function') return false;
+
+  // If has modified prototype
+  prot = ctor.prototype;
+  if (isObjectObject(prot) === false) return false;
+
+  // If constructor does not have an Object-specific method
+  if (prot.hasOwnProperty('isPrototypeOf') === false) {
+    return false;
+  }
+
+  // Most likely a plain Object
+  return true;
+}
+
+module.exports = isPlainObject;
+
+
+/***/ }),
+
+/***/ 631:
+/***/ (function(module) {
+
+module.exports = require("net");
+
+/***/ }),
+
+/***/ 649:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = getLastPage
+
+const getPage = __webpack_require__(265)
+
+function getLastPage (octokit, link, headers) {
+  return getPage(octokit, link, 'last', headers)
+}
+
+
+/***/ }),
+
+/***/ 654:
+/***/ (function(module) {
+
+// This is not the set of all possible signals.
+//
+// It IS, however, the set of all signals that trigger
+// an exit on either Linux or BSD systems.  Linux is a
+// superset of the signal names supported on BSD, and
+// the unknown signals just fail to register, so we can
+// catch that easily enough.
+//
+// Don't bother with SIGKILL.  It's uncatchable, which
+// means that we can't fire any callbacks anyway.
+//
+// If a user does happen to register a handler on a non-
+// fatal signal like SIGWINCH or something, and then
+// exit, it'll end up firing `process.emit('exit')`, so
+// the handler will be fired anyway.
+//
+// SIGBUS, SIGFPE, SIGSEGV and SIGILL, when not raised
+// artificially, inherently leave the process in a
+// state from which it is not safe to try and enter JS
+// listeners.
+module.exports = [
+  'SIGABRT',
+  'SIGALRM',
+  'SIGHUP',
+  'SIGINT',
+  'SIGTERM'
+]
+
+if (process.platform !== 'win32') {
+  module.exports.push(
+    'SIGVTALRM',
+    'SIGXCPU',
+    'SIGXFSZ',
+    'SIGUSR2',
+    'SIGTRAP',
+    'SIGSYS',
+    'SIGQUIT',
+    'SIGIOT'
+    // should detect profiler and enable/disable accordingly.
+    // see #21
+    // 'SIGPROF'
+  )
+}
+
+if (process.platform === 'linux') {
+  module.exports.push(
+    'SIGIO',
+    'SIGPOLL',
+    'SIGPWR',
+    'SIGSTKFLT',
+    'SIGUNUSED'
+  )
+}
+
+
+/***/ }),
+
+/***/ 669:
+/***/ (function(module) {
+
+module.exports = require("util");
+
+/***/ }),
+
+/***/ 674:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = authenticate;
+
+const { Deprecation } = __webpack_require__(692);
+const once = __webpack_require__(969);
+
+const deprecateAuthenticate = once((log, deprecation) => log.warn(deprecation));
+
+function authenticate(state, options) {
+  deprecateAuthenticate(
+    state.octokit.log,
+    new Deprecation(
+      '[@octokit/rest] octokit.authenticate() is deprecated. Use "auth" constructor option instead.'
+    )
+  );
+
+  if (!options) {
+    state.auth = false;
+    return;
+  }
+
+  switch (options.type) {
+    case "basic":
+      if (!options.username || !options.password) {
+        throw new Error(
+          "Basic authentication requires both a username and password to be set"
+        );
+      }
+      break;
+
+    case "oauth":
+      if (!options.token && !(options.key && options.secret)) {
+        throw new Error(
+          "OAuth2 authentication requires a token or key & secret to be set"
+        );
+      }
+      break;
+
+    case "token":
+    case "app":
+      if (!options.token) {
+        throw new Error("Token authentication requires a token to be set");
+      }
+      break;
+
+    default:
+      throw new Error(
+        "Invalid authentication type, must be 'basic', 'oauth', 'token' or 'app'"
+      );
+  }
+
+  state.auth = options;
+}
+
+
+/***/ }),
+
+/***/ 675:
+/***/ (function(module) {
+
+module.exports = function btoa(str) {
+  return new Buffer(str).toString('base64')
+}
+
+
+/***/ }),
+
+/***/ 692:
+/***/ (function(__unusedmodule, exports) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+class Deprecation extends Error {
+  constructor(message) {
+    super(message); // Maintains proper stack trace (only available on V8)
+
+    /* istanbul ignore next */
+
+    if (Error.captureStackTrace) {
+      Error.captureStackTrace(this, this.constructor);
+    }
+
+    this.name = 'Deprecation';
+  }
+
+}
+
+exports.Deprecation = Deprecation;
+
+
+/***/ }),
+
+/***/ 697:
+/***/ (function(module) {
+
+"use strict";
+
+module.exports = (promise, onFinally) => {
+	onFinally = onFinally || (() => {});
+
+	return promise.then(
+		val => new Promise(resolve => {
+			resolve(onFinally());
+		}).then(() => val),
+		err => new Promise(resolve => {
+			resolve(onFinally());
+		}).then(() => {
+			throw err;
+		})
+	);
+};
+
+
+/***/ }),
+
+/***/ 742:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+var fs = __webpack_require__(747)
+var core
+if (process.platform === 'win32' || global.TESTING_WINDOWS) {
+  core = __webpack_require__(818)
+} else {
+  core = __webpack_require__(197)
+}
+
+module.exports = isexe
+isexe.sync = sync
+
+function isexe (path, options, cb) {
+  if (typeof options === 'function') {
+    cb = options
+    options = {}
+  }
+
+  if (!cb) {
+    if (typeof Promise !== 'function') {
+      throw new TypeError('callback not provided')
+    }
+
+    return new Promise(function (resolve, reject) {
+      isexe(path, options || {}, function (er, is) {
+        if (er) {
+          reject(er)
+        } else {
+          resolve(is)
+        }
+      })
+    })
+  }
+
+  core(path, options || {}, function (er, is) {
+    // ignore EACCES because that just means we aren't allowed to run it
+    if (er) {
+      if (er.code === 'EACCES' || options && options.ignoreErrors) {
+        er = null
+        is = false
+      }
+    }
+    cb(er, is)
+  })
+}
+
+function sync (path, options) {
+  // my kingdom for a filtered catch
+  try {
+    return core.sync(path, options || {})
+  } catch (er) {
+    if (options && options.ignoreErrors || er.code === 'EACCES') {
+      return false
+    } else {
+      throw er
+    }
+  }
+}
+
+
+/***/ }),
+
+/***/ 747:
+/***/ (function(module) {
+
+module.exports = require("fs");
+
+/***/ }),
+
+/***/ 753:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var endpoint = __webpack_require__(385);
+var universalUserAgent = __webpack_require__(796);
+var isPlainObject = _interopDefault(__webpack_require__(548));
+var nodeFetch = _interopDefault(__webpack_require__(369));
+var requestError = __webpack_require__(463);
+
+const VERSION = "5.3.1";
+
+function getBufferResponse(response) {
+  return response.arrayBuffer();
+}
+
+function fetchWrapper(requestOptions) {
+  if (isPlainObject(requestOptions.body) || Array.isArray(requestOptions.body)) {
+    requestOptions.body = JSON.stringify(requestOptions.body);
+  }
+
+  let headers = {};
+  let status;
+  let url;
+  const fetch = requestOptions.request && requestOptions.request.fetch || nodeFetch;
+  return fetch(requestOptions.url, Object.assign({
+    method: requestOptions.method,
+    body: requestOptions.body,
+    headers: requestOptions.headers,
+    redirect: requestOptions.redirect
+  }, requestOptions.request)).then(response => {
+    url = response.url;
+    status = response.status;
+
+    for (const keyAndValue of response.headers) {
+      headers[keyAndValue[0]] = keyAndValue[1];
+    }
+
+    if (status === 204 || status === 205) {
+      return;
+    } // GitHub API returns 200 for HEAD requsets
+
+
+    if (requestOptions.method === "HEAD") {
+      if (status < 400) {
+        return;
+      }
+
+      throw new requestError.RequestError(response.statusText, status, {
+        headers,
+        request: requestOptions
+      });
+    }
+
+    if (status === 304) {
+      throw new requestError.RequestError("Not modified", status, {
+        headers,
+        request: requestOptions
+      });
+    }
+
+    if (status >= 400) {
+      return response.text().then(message => {
+        const error = new requestError.RequestError(message, status, {
+          headers,
+          request: requestOptions
+        });
+
+        try {
+          let responseBody = JSON.parse(error.message);
+          Object.assign(error, responseBody);
+          let errors = responseBody.errors; // Assumption `errors` would always be in Array Fotmat
+
+          error.message = error.message + ": " + errors.map(JSON.stringify).join(", ");
+        } catch (e) {// ignore, see octokit/rest.js#684
+        }
+
+        throw error;
+      });
+    }
+
+    const contentType = response.headers.get("content-type");
+
+    if (/application\/json/.test(contentType)) {
+      return response.json();
+    }
+
+    if (!contentType || /^text\/|charset=utf-8$/.test(contentType)) {
+      return response.text();
+    }
+
+    return getBufferResponse(response);
+  }).then(data => {
+    return {
+      status,
+      url,
+      headers,
+      data
+    };
+  }).catch(error => {
+    if (error instanceof requestError.RequestError) {
+      throw error;
+    }
+
+    throw new requestError.RequestError(error.message, 500, {
+      headers,
+      request: requestOptions
+    });
+  });
+}
+
+function withDefaults(oldEndpoint, newDefaults) {
+  const endpoint = oldEndpoint.defaults(newDefaults);
+
+  const newApi = function (route, parameters) {
+    const endpointOptions = endpoint.merge(route, parameters);
+
+    if (!endpointOptions.request || !endpointOptions.request.hook) {
+      return fetchWrapper(endpoint.parse(endpointOptions));
+    }
+
+    const request = (route, parameters) => {
+      return fetchWrapper(endpoint.parse(endpoint.merge(route, parameters)));
+    };
+
+    Object.assign(request, {
+      endpoint,
+      defaults: withDefaults.bind(null, endpoint)
+    });
+    return endpointOptions.request.hook(request, endpointOptions);
+  };
+
+  return Object.assign(newApi, {
+    endpoint,
+    defaults: withDefaults.bind(null, endpoint)
+  });
+}
+
+const request = withDefaults(endpoint.endpoint, {
+  headers: {
+    "user-agent": `octokit-request.js/${VERSION} ${universalUserAgent.getUserAgent()}`
+  }
+});
+
+exports.request = request;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 761:
+/***/ (function(module) {
+
+module.exports = require("zlib");
+
+/***/ }),
+
+/***/ 763:
+/***/ (function(module) {
+
+module.exports = removeHook
+
+function removeHook (state, name, method) {
+  if (!state.registry[name]) {
+    return
+  }
+
+  var index = state.registry[name]
+    .map(function (registered) { return registered.orig })
+    .indexOf(method)
+
+  if (index === -1) {
+    return
+  }
+
+  state.registry[name].splice(index, 1)
+}
+
+
+/***/ }),
+
+/***/ 768:
+/***/ (function(module) {
+
+"use strict";
+
+module.exports = function (x) {
+	var lf = typeof x === 'string' ? '\n' : '\n'.charCodeAt();
+	var cr = typeof x === 'string' ? '\r' : '\r'.charCodeAt();
+
+	if (x[x.length - 1] === lf) {
+		x = x.slice(0, x.length - 1);
+	}
+
+	if (x[x.length - 1] === cr) {
+		x = x.slice(0, x.length - 1);
+	}
+
+	return x;
+};
+
+
+/***/ }),
+
+/***/ 777:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = getFirstPage
+
+const getPage = __webpack_require__(265)
+
+function getFirstPage (octokit, link, headers) {
+  return getPage(octokit, link, 'first', headers)
+}
+
+
+/***/ }),
+
+/***/ 796:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+function _interopDefault (ex) { return (ex && (typeof ex === 'object') && 'default' in ex) ? ex['default'] : ex; }
+
+var osName = _interopDefault(__webpack_require__(2));
+
+function getUserAgent() {
+  try {
+    return `Node.js/${process.version.substr(1)} (${osName()}; ${process.arch})`;
+  } catch (error) {
+    if (/wmic os get Caption/.test(error.message)) {
+      return "Windows <version undetectable>";
+    }
+
+    throw error;
+  }
+}
+
+exports.getUserAgent = getUserAgent;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 813:
+/***/ (function(__unusedmodule, exports) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+async function auth(token) {
+  const tokenType = token.split(/\./).length === 3 ? "app" : /^v\d+\./.test(token) ? "installation" : "oauth";
+  return {
+    type: "token",
+    token: token,
+    tokenType
+  };
+}
+
+/**
+ * Prefix token for usage in the Authorization header
+ *
+ * @param token OAuth token or JSON Web Token
+ */
+function withAuthorizationPrefix(token) {
+  if (token.split(/\./).length === 3) {
+    return `bearer ${token}`;
+  }
+
+  return `token ${token}`;
+}
+
+async function hook(token, request, route, parameters) {
+  const endpoint = request.endpoint.merge(route, parameters);
+  endpoint.headers.authorization = withAuthorizationPrefix(token);
+  return request(endpoint);
+}
+
+const createTokenAuth = function createTokenAuth(token) {
+  if (!token) {
+    throw new Error("[@octokit/auth-token] No token passed to createTokenAuth");
+  }
+
+  if (typeof token !== "string") {
+    throw new Error("[@octokit/auth-token] Token passed to createTokenAuth is not a string");
+  }
+
+  token = token.replace(/^(token|bearer) +/i, "");
+  return Object.assign(auth.bind(null, token), {
+    hook: hook.bind(null, token)
+  });
+};
+
+exports.createTokenAuth = createTokenAuth;
+//# sourceMappingURL=index.js.map
+
+
+/***/ }),
+
+/***/ 814:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = which
+which.sync = whichSync
+
+var isWindows = process.platform === 'win32' ||
+    process.env.OSTYPE === 'cygwin' ||
+    process.env.OSTYPE === 'msys'
+
+var path = __webpack_require__(622)
+var COLON = isWindows ? ';' : ':'
+var isexe = __webpack_require__(742)
+
+function getNotFoundError (cmd) {
+  var er = new Error('not found: ' + cmd)
+  er.code = 'ENOENT'
+
+  return er
+}
+
+function getPathInfo (cmd, opt) {
+  var colon = opt.colon || COLON
+  var pathEnv = opt.path || process.env.PATH || ''
+  var pathExt = ['']
+
+  pathEnv = pathEnv.split(colon)
+
+  var pathExtExe = ''
+  if (isWindows) {
+    pathEnv.unshift(process.cwd())
+    pathExtExe = (opt.pathExt || process.env.PATHEXT || '.EXE;.CMD;.BAT;.COM')
+    pathExt = pathExtExe.split(colon)
+
+
+    // Always test the cmd itself first.  isexe will check to make sure
+    // it's found in the pathExt set.
+    if (cmd.indexOf('.') !== -1 && pathExt[0] !== '')
+      pathExt.unshift('')
+  }
+
+  // If it has a slash, then we don't bother searching the pathenv.
+  // just check the file itself, and that's it.
+  if (cmd.match(/\//) || isWindows && cmd.match(/\\/))
+    pathEnv = ['']
+
+  return {
+    env: pathEnv,
+    ext: pathExt,
+    extExe: pathExtExe
+  }
+}
+
+function which (cmd, opt, cb) {
+  if (typeof opt === 'function') {
+    cb = opt
+    opt = {}
+  }
+
+  var info = getPathInfo(cmd, opt)
+  var pathEnv = info.env
+  var pathExt = info.ext
+  var pathExtExe = info.extExe
+  var found = []
+
+  ;(function F (i, l) {
+    if (i === l) {
+      if (opt.all && found.length)
+        return cb(null, found)
+      else
+        return cb(getNotFoundError(cmd))
+    }
+
+    var pathPart = pathEnv[i]
+    if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
+      pathPart = pathPart.slice(1, -1)
+
+    var p = path.join(pathPart, cmd)
+    if (!pathPart && (/^\.[\\\/]/).test(cmd)) {
+      p = cmd.slice(0, 2) + p
+    }
+    ;(function E (ii, ll) {
+      if (ii === ll) return F(i + 1, l)
+      var ext = pathExt[ii]
+      isexe(p + ext, { pathExt: pathExtExe }, function (er, is) {
+        if (!er && is) {
+          if (opt.all)
+            found.push(p + ext)
+          else
+            return cb(null, p + ext)
+        }
+        return E(ii + 1, ll)
+      })
+    })(0, pathExt.length)
+  })(0, pathEnv.length)
+}
+
+function whichSync (cmd, opt) {
+  opt = opt || {}
+
+  var info = getPathInfo(cmd, opt)
+  var pathEnv = info.env
+  var pathExt = info.ext
+  var pathExtExe = info.extExe
+  var found = []
+
+  for (var i = 0, l = pathEnv.length; i < l; i ++) {
+    var pathPart = pathEnv[i]
+    if (pathPart.charAt(0) === '"' && pathPart.slice(-1) === '"')
+      pathPart = pathPart.slice(1, -1)
+
+    var p = path.join(pathPart, cmd)
+    if (!pathPart && /^\.[\\\/]/.test(cmd)) {
+      p = cmd.slice(0, 2) + p
+    }
+    for (var j = 0, ll = pathExt.length; j < ll; j ++) {
+      var cur = p + pathExt[j]
+      var is
+      try {
+        is = isexe.sync(cur, { pathExt: pathExtExe })
+        if (is) {
+          if (opt.all)
+            found.push(cur)
+          else
+            return cur
+        }
+      } catch (ex) {}
+    }
+  }
+
+  if (opt.all && found.length)
+    return found
+
+  if (opt.nothrow)
+    return null
+
+  throw getNotFoundError(cmd)
+}
+
+
+/***/ }),
+
+/***/ 816:
+/***/ (function(module) {
+
+"use strict";
+
+module.exports = /^#!.*/;
+
+
+/***/ }),
+
+/***/ 818:
+/***/ (function(module, __unusedexports, __webpack_require__) {
+
+module.exports = isexe
+isexe.sync = sync
+
+var fs = __webpack_require__(747)
+
+function checkPathExt (path, options) {
+  var pathext = options.pathExt !== undefined ?
+    options.pathExt : process.env.PATHEXT
+
+  if (!pathext) {
+    return true
+  }
+
+  pathext = pathext.split(';')
+  if (pathext.indexOf('') !== -1) {
+    return true
+  }
+  for (var i = 0; i < pathext.length; i++) {
+    var p = pathext[i].toLowerCase()
+    if (p && path.substr(-p.length).toLowerCase() === p) {
+      return true
+    }
+  }
+  return false
+}
+
+function checkStat (stat, path, options) {
+  if (!stat.isSymbolicLink() && !stat.isFile()) {
+    return false
+  }
+  return checkPathExt(path, options)
+}
+
+function isexe (path, options, cb) {
+  fs.stat(path, function (er, stat) {
+    cb(er, er ? false : checkStat(stat, path, options))
+  })
+}
+
+function sync (path, options) {
+  return checkStat(fs.statSync(path), path, options)
+}
+
+
+/***/ }),
+
+/***/ 835:
+/***/ (function(module) {
+
+module.exports = require("url");
+
+/***/ }),
+
+/***/ 842:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+
+Object.defineProperty(exports, '__esModule', { value: true });
+
+var deprecation = __webpack_require__(692);
+
+var endpointsByScope = {
+  actions: {
+    cancelWorkflowRun: {
+      method: "POST",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        run_id: {
+          required: true,
+          type: "integer"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runs/:run_id/cancel"
+    },
+    createOrUpdateSecretForRepo: {
+      method: "PUT",
+      params: {
+        encrypted_value: {
+          type: "string"
+        },
+        key_id: {
+          type: "string"
+        },
+        name: {
+          required: true,
+          type: "string"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/secrets/:name"
+    },
+    createRegistrationToken: {
+      method: "POST",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runners/registration-token"
+    },
+    createRemoveToken: {
+      method: "POST",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runners/remove-token"
+    },
+    deleteArtifact: {
+      method: "DELETE",
+      params: {
+        artifact_id: {
+          required: true,
+          type: "integer"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/artifacts/:artifact_id"
+    },
+    deleteSecretFromRepo: {
+      method: "DELETE",
+      params: {
+        name: {
+          required: true,
+          type: "string"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/secrets/:name"
+    },
+    downloadArtifact: {
+      method: "GET",
+      params: {
+        archive_format: {
+          required: true,
+          type: "string"
+        },
+        artifact_id: {
+          required: true,
+          type: "integer"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/artifacts/:artifact_id/:archive_format"
+    },
+    getArtifact: {
+      method: "GET",
+      params: {
+        artifact_id: {
+          required: true,
+          type: "integer"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/artifacts/:artifact_id"
+    },
+    getPublicKey: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/secrets/public-key"
+    },
+    getSecret: {
+      method: "GET",
+      params: {
+        name: {
+          required: true,
+          type: "string"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        page: {
+          type: "integer"
+        },
+        per_page: {
+          type: "integer"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/secrets/:name"
+    },
+    getSelfHostedRunner: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        runner_id: {
+          required: true,
+          type: "integer"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runners/:runner_id"
+    },
+    getWorkflow: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        workflow_id: {
+          required: true,
+          type: "integer"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/workflows/:workflow_id"
+    },
+    getWorkflowJob: {
+      method: "GET",
+      params: {
+        job_id: {
+          required: true,
+          type: "integer"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/jobs/:job_id"
+    },
+    getWorkflowRun: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        run_id: {
+          required: true,
+          type: "integer"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runs/:run_id"
+    },
+    listDownloadsForSelfHostedRunnerApplication: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runners/downloads"
+    },
+    listJobsForWorkflowRun: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        page: {
+          type: "integer"
+        },
+        per_page: {
+          type: "integer"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        run_id: {
+          required: true,
+          type: "integer"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runs/:run_id/jobs"
+    },
+    listRepoWorkflowRuns: {
+      method: "GET",
+      params: {
+        actor: {
+          type: "string"
+        },
+        branch: {
+          type: "string"
+        },
+        event: {
+          type: "string"
+        },
+        owner: {
+          required: true,
+          type: "string"
+        },
+        page: {
+          type: "integer"
+        },
+        per_page: {
+          type: "integer"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        },
+        status: {
+          enum: ["completed", "status", "conclusion"],
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/runs"
+    },
+    listRepoWorkflows: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        page: {
+          type: "integer"
+        },
+        per_page: {
+          type: "integer"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/workflows"
+    },
+    listSecretsForRepo: {
+      method: "GET",
+      params: {
+        owner: {
+          required: true,
+          type: "string"
+        },
+        page: {
+          type: "integer"
+        },
+        per_page: {
+          type: "integer"
+        },
+        repo: {
+          required: true,
+          type: "string"
+        }
+      },
+      url: "/repos/:owner/:repo/actions/secrets"
+    },
+    listSelfHostedRunnersForRepo: {
+      method: "GET",
+      params: {
+        owner: {
... 22625 lines suppressed ...


[airflow-cancel-workflow-runs] 04/44: Add additional validation

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 6595731233df96cc183f56db9db19a4313e7febf
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 4 03:26:38 2020 -0600

    Add additional validation
---
 dist/index.js | 16 +++++++++++++---
 src/main.ts   | 18 +++++++++++++++---
 2 files changed, 28 insertions(+), 6 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 88b1aee..3995113 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1472,14 +1472,24 @@ function run() {
             const token = core.getInput('token');
             const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
             const repository = getRequiredEnv('GITHUB_REPOSITORY');
+            const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
             const [owner, repo] = repository.split('/');
-            const refsPrefix = 'refs/heads/';
+            const branchPrefix = 'refs/heads/';
+            const tagPrefix = 'refs/tags/';
+            if (eventName !== 'push') {
+                core.info('Skipping non-push event');
+                return;
+            }
             let branch = getRequiredEnv('GITHUB_REF');
-            if (!branch.startsWith(refsPrefix)) {
+            if (!branch.startsWith(branchPrefix)) {
+                if (branch.startsWith(tagPrefix)) {
+                    core.info(`Skipping tag build`);
+                    return;
+                }
                 const message = `${branch} was not an expected branch ref (refs/heads/).`;
                 throw new Error(message);
             }
-            branch = branch.replace(refsPrefix, '');
+            branch = branch.replace(branchPrefix, '');
             core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
             const octokit = new github.GitHub(token);
             const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
diff --git a/src/main.ts b/src/main.ts
index 44db6fe..bfe9c55 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -7,15 +7,27 @@ async function run(): Promise<void> {
 
     const selfRunId = getRequiredEnv('GITHUB_RUN_ID')
     const repository = getRequiredEnv('GITHUB_REPOSITORY')
+    const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
+
     const [owner, repo] = repository.split('/')
-    const refsPrefix = 'refs/heads/'
+    const branchPrefix = 'refs/heads/'
+    const tagPrefix = 'refs/tags/'
+
+    if (eventName !== 'push') {
+      core.info('Skipping non-push event')
+      return
+    }
 
     let branch = getRequiredEnv('GITHUB_REF')
-    if (!branch.startsWith(refsPrefix)) {
+    if (!branch.startsWith(branchPrefix)) {
+      if (branch.startsWith(tagPrefix)) {
+        core.info(`Skipping tag build`)
+        return
+      }
       const message = `${branch} was not an expected branch ref (refs/heads/).`
       throw new Error(message)
     }
-    branch = branch.replace(refsPrefix, '')
+    branch = branch.replace(branchPrefix, '')
 
     core.info(
       `Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`


[airflow-cancel-workflow-runs] 39/44: Adds 'allDuplicates' mode of cancelling (#10)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 99869d37d982384d18c79539b67df94f17557cbe
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Oct 31 18:18:25 2020 +0100

    Adds 'allDuplicates' mode of cancelling (#10)
---
 README.md     |   81 +-
 action.yml    |   17 +-
 dist/index.js | 4723 ++++++++-------------------------------------------------
 src/main.ts   | 1325 +++++++++++-----
 4 files changed, 1673 insertions(+), 4473 deletions(-)

diff --git a/README.md b/README.md
index 0a13160..e81c66b 100644
--- a/README.md
+++ b/README.md
@@ -14,7 +14,8 @@
 - [Inputs and outputs](#inputs-and-outputs)
   - [Inputs](#inputs)
   - [Outputs](#outputs)
-- [Examples](#examples)
+  - [Most often used canceling example](#most-often-used-canceling-example)
+- [More Examples](#more-examples)
   - [Repositories that use Pull Requests from forks](#repositories-that-use-pull-requests-from-forks)
     - [Cancel duplicate runs for the source workflow](#cancel-duplicate-runs-for-the-source-workflow)
     - [Cancel duplicate jobs for triggered workflow](#cancel-duplicate-jobs-for-triggered-workflow)
@@ -98,10 +99,6 @@ If you want a comprehensive solution, you should use the action as follows:
    The `workflow_run` should be responsible for all canceling actions. The examples below show
    the possible ways the action can be utilized.
 
-The previous version of this action (v1) used `schedule` events to cancel previous runs. With
-the announcement of pull_request_target by GitHub, the v2 version discourages using this pattern
-and `schedule` events are no longer needed.
-
 # Inputs and outputs
 
 ## Inputs
@@ -122,12 +119,13 @@ and `schedule` events are no longer needed.
 
 The job cancel modes work as follows:
 
-| Cancel Mode  | No `sourceRunId` specified                                             | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                      |
-|--------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
-| `duplicates` | Cancels past, duplicate runs from the same repo/branch as current run. | Cancels past, duplicate runs for the same repo/branch as the source run.            |
-| `self`       | Cancels self run.                                                      | Cancel the `sourceRunId` run.                                                       |
-| `failedJobs` | Cancels all runs of own workflow that have matching jobs that failed.  | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
-| `namedJobs`  | Cancels all runs of own workflow that have matching jobs.              | Cancels all runs of the `sourceRunId` workflow that have matching jobs.             |
+| Cancel Mode     | No `sourceRunId` specified                                             | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                      |
+|-----------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| `duplicates`    | Cancels duplicate runs from the same repo/branch as current run.       | Cancels duplicate runs for the same repo/branch as the source run.                  |
+| `allDuplicates` | Cancels duplicate runs from all running workflows.                     | Cancels duplicate runs from all running workflows.                                  |
+| `self`          | Cancels self run.                                                      | Cancel the `sourceRunId` run.                                                       |
+| `failedJobs`    | Cancels all runs of own workflow that have matching jobs that failed.  | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
+| `namedJobs`     | Cancels all runs of own workflow that have matching jobs.              | Cancels all runs of the `sourceRunId` workflow that have matching jobs.             |
 
 
 ## Outputs
@@ -143,7 +141,38 @@ The job cancel modes work as follows:
 | `sourceEvent`       | Current event: ``${{ github.event }}``                  | Event of the run that triggered this `workflow_run`                                                  |
 | `cancelledRuns`     | JSON-stringified array of cancelled run ids.            | JSON-stringified array of cancelled run ids.                                                         |
 
-# Examples
+## Most often used canceling example
+
+The most common canceling example is that you want to cancel all duplicates appearing in your build queue.
+As of 4.1 version of the Action this can be realised by single workflow run that can cancel all duplicates
+for all running workflows. It is resistant to temporary queues - as it can cancel also the future, queued
+workflows that have duplicated, fresher (also queued workflows and this is recommended for everyone.
+
+The below example is a "workflow_run" type of event. The workflow_run event always has "write" access that allows
+it to cancel other workflows - even if they are coming from pull request.
+
+```yaml
+name: Cancelling Duplicates
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-duplicate-workflow-runs:
+    name: "Cancel duplicate workflow runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v4_1
+        name: "Cancel duplicate workflow runs"
+        with:
+          cancelMode: allDuplicates
+          token: ${{ secrets.GITHUB_TOKEN }}
+          sourceRunId: ${{ github.event.workflow_run.id }}
+```
+
+
+# More Examples
 
 Note that you can combine the steps below in several steps of the same job. The examples here are showing
 one step per case for clarity.
@@ -202,7 +231,7 @@ jobs:
     name: "Cancel duplicate workflow runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
@@ -263,7 +292,7 @@ jobs:
       sourceHeadSha: ${{ steps.cancel.outputs.sourceHeadSha }}
       sourceEvent: ${{ steps.cancel.outputs.sourceEvent }}
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         id: cancel
         name: "Cancel duplicate CI runs"
         with:
@@ -276,7 +305,7 @@ jobs:
             that you will not see in the list of checks.
 
             You can checks the status of those images in:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Cancel duplicate Cancelling runs"
         with:
           cancelMode: namedJobs
@@ -326,7 +355,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self CI workflow run"
-        uses: potiuk/cancel-workflow-runs@v2
+        uses: potiuk/cancel-workflow-runs@v4_1
         with:
           cancelMode: self
           notifyPRCancel: true
@@ -355,7 +384,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self Cancelling workflow run"
-        uses: potiuk/cancel-workflow-runs@v2
+        uses: potiuk/cancel-workflow-runs@v4_1
         with:
           cancelMode: self
           notifyPRCancel: true
@@ -388,7 +417,7 @@ jobs:
     name: "Fail fast CI runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Fail fast CI runs"
         with:
           cancelMode: failedJobs
@@ -429,7 +458,7 @@ jobs:
     name: "Fail fast CI runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Fail fast CI. Source run: ${{ github.event.workflow_run.id }}"
         id: cancel-failed
         with:
@@ -452,7 +481,7 @@ jobs:
             echo "::set-output name=matching-regexp::${REGEXP}"
       - name: "Cancel triggered 'Cancelling' runs for the cancelled failed runs"
         if: steps.cancel-failed.outputs.cancelledRuns != '[]'
-        uses: potiuk/cancel-workflow-runs@master
+        uses: potiuk/cancel-workflow-runs@v4_1
         with:
           cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -487,7 +516,7 @@ jobs:
     name: "Fail fast Canceling runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Fail fast Canceling runs"
         with:
           cancelMode: failedJobs
@@ -512,7 +541,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self CI workflow run"
-        uses: potiuk/cancel-workflow-runs@v2
+        uses: potiuk/cancel-workflow-runs@v4_1
         with:
           cancelMode: duplicates
           cancelFutureDuplicates: true
@@ -553,7 +582,7 @@ jobs:
     name: "Cancel duplicate workflow runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
@@ -577,7 +606,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self workflow run"
-        uses: potiuk/cancel-workflow-runs@v2
+        uses: potiuk/cancel-workflow-runs@v4_1
         with:
           cancelMode: self
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -603,7 +632,7 @@ jobs:
     name: "Cancel failed runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Cancel failed runs"
         with:
           cancelMode: failedJobs
@@ -635,7 +664,7 @@ jobs:
     name: "Cancel the self workflow run"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v2
+      - uses: potiuk/cancel-workflow-runs@v4_1
         name: "Cancel past CI runs"
         with:
           cancelMode: namedJobs
diff --git a/action.yml b/action.yml
index aaec312..541094a 100644
--- a/action.yml
+++ b/action.yml
@@ -29,12 +29,17 @@ inputs:
   cancelMode:
     description: |
       The mode of cancel. One of:
-          * `duplicates`  - cancels past, duplicate runs from the same repo/branch as local run or
-                            sourceId workflow. This is the default mode when cancelMode is not specified.
-          * `self`        - cancels self run - either own run if sourceRunId is not set, or
-                            the source run that triggered the `workflow_run'
-          * `failedJobs`  - cancels all runs that failed in jobs matching one of the regexps
-          * `namedJobs`   - cancels runs where names of some jobs match some of regexps
+          * `duplicates`    - cancels duplicate runs from the same repo/branch as local run or
+                              sourceId workflow. This is the default mode when cancelMode is not specified.
+          * `allDuplicates` - cancels duplicate runs from all workflows. It is more aggressive version of
+                              duplicate canceling - as it cancels all duplicates. It is helpful in case
+                              of long queues of builds - as it is enough that one of the workflows that
+                              cancel duplicates is executed, it can effectively clean-up the queue in this
+                              case for all the future, queued runs.
+          * `self`          - cancels self run - either own run if sourceRunId is not set, or
+                              the source run that triggered the `workflow_run'
+          * `failedJobs`    - cancels all runs that failed in jobs matching one of the regexps
+          * `namedJobs`     - cancels runs where names of some jobs match some of regexps
     required: false
   cancelFutureDuplicates:
     description: |
diff --git a/dist/index.js b/dist/index.js
index 69bd164..6f047aa 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1465,76 +1465,138 @@ var __importStar = (this && this.__importStar) || function (mod) {
 Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
-const treemap = __importStar(__webpack_require__(706));
-const CANCELLABLE_RUNS = [
+/**
+ Those are the cancellable event types tht we know about
+ */
+const CANCELLABLE_EVENT_TYPES = [
     'push',
     'pull_request',
     'workflow_run',
     'schedule',
     'workflow_dispatch'
 ];
+/**
+ * Those are different modes for cancelling
+ */
 var CancelMode;
 (function (CancelMode) {
     CancelMode["DUPLICATES"] = "duplicates";
+    CancelMode["ALL_DUPLICATES"] = "allDuplicates";
     CancelMode["SELF"] = "self";
     CancelMode["FAILED_JOBS"] = "failedJobs";
     CancelMode["NAMED_JOBS"] = "namedJobs";
 })(CancelMode || (CancelMode = {}));
-function createListRunsQueryOtherRuns(octokit, owner, repo, status, workflowId, headBranch, eventName) {
+/**
+ * Converts the source of a run object into a string that can be used as map key in maps where we keep
+ * arrays of runs per source group
+ * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @returns the unique string id for the source group
+ */
+function getSourceGroupId(sourceGroup) {
+    return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`;
+}
+/**
+ * Creates query parameters selecting all runs that share the same source group as we have. This can
+ * be used to select duplicates of my own run.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param mySourceGroup - source group of the originating run
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
+function createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup) {
     const request = {
-        owner,
-        repo,
+        owner: repositoryInfo.owner,
+        repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: workflowId,
+        workflow_id: mySourceGroup.workflowId,
         status,
-        branch: headBranch,
-        event: eventName
+        branch: mySourceGroup.headBranch,
+        event: mySourceGroup.eventName
     };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+    return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
 }
-function createListRunsQueryMyOwnRun(octokit, owner, repo, status, workflowId, runId) {
+/**
+ * Creates query parameters selecting only specific run Id.
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param workflowId - Id of the workflow to retrieve
+ * @param runId - run Id to retrieve
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
+function createListRunsQuerySpecificRunId(repositoryInfo, status, workflowId, runId) {
     const request = {
-        owner,
-        repo,
+        owner: repositoryInfo.owner,
+        repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
         workflow_id: workflowId,
         status,
         // eslint-disable-next-line @typescript-eslint/camelcase
         run_id: runId.toString()
     };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+    return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
 }
-function createListRunsQueryAllRuns(octokit, owner, repo, status, workflowId) {
+/**
+ * Creates query parameters selecting all run Ids for specified workflow Id.
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param workflowId - Id of the workflow to retrieve
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
+function createListRunsQueryAllRuns(repositoryInfo, status, workflowId) {
     const request = {
-        owner,
-        repo,
+        owner: repositoryInfo.owner,
+        repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
         workflow_id: workflowId,
         status
     };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+    return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
 }
-function createJobsForWorkflowRunQuery(octokit, owner, repo, runId) {
+/**
+ * Creates query parameters selecting all jobs for specified run Id.
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @return query parameters merged with the listJobsForWorkflowRun criteria
+ */
+function createJobsForWorkflowRunQuery(repositoryInfo, runId) {
     const request = {
-        owner,
-        repo,
+        owner: repositoryInfo.owner,
+        repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
         run_id: runId
     };
-    return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request);
+    return repositoryInfo.octokit.actions.listJobsForWorkflowRun.endpoint.merge(request);
 }
-function matchInArray(s, regexps) {
+/**
+ * Returns true if the string matches any of the regexps in array of regexps
+ * @param stringToMatch string to match
+ * @param regexps array of regexp to match the string against
+ * @return true if there is a match
+ */
+function matchInArray(stringToMatch, regexps) {
     for (const regexp of regexps) {
-        if (s.match(regexp)) {
+        if (stringToMatch.match(regexp)) {
             return true;
         }
     }
     return false;
 }
-function jobsMatchingNames(octokit, owner, repo, runId, jobNameRegexps, checkIfFailed) {
+/**
+ * Returns true if the runId specified has jobs matching the regexp and optionally checks if those
+ * jobs are failed.
+ * * If checkIfFailed is False, it returns true if any of the job name for the run match any of the regexps
+ * * Id checkIfFailed is True, it returns true if any of the matching jobs have status 'failed'
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @param jobNameRegexps - array of job name regexps
+ * @param checkIfFailed - whether to check the 'failed' status of matched jobs
+ * @return true if there is a match
+ */
+function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed) {
     var e_1, _a;
     return __awaiter(this, void 0, void 0, function* () {
-        const listJobs = createJobsForWorkflowRunQuery(octokit, owner, repo, runId);
+        const listJobs = createJobsForWorkflowRunQuery(repositoryInfo, runId);
         if (checkIfFailed) {
             core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps} that failed\n`);
         }
@@ -1542,7 +1604,7 @@ function jobsMatchingNames(octokit, owner, repo, runId, jobNameRegexps, checkIfF
             core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`);
         }
         try {
-            for (var _b = __asyncValues(octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
+            for (var _b = __asyncValues(repositoryInfo.octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
                 const item = _c.value;
                 for (const job of item.data.jobs) {
                     core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`);
@@ -1550,11 +1612,13 @@ function jobsMatchingNames(octokit, owner, repo, runId, jobNameRegexps, checkIfF
                         if (checkIfFailed) {
                             // Only fail the build if one of the matching jobs fail
                             if (job.conclusion === 'failure') {
-                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed. Cancelling run.`);
+                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
+                                    ` Cancelling run.`);
                                 return true;
                             }
                             else {
-                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. So far, so good.`);
+                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
+                                    ` So far, so good.`);
                             }
                         }
                         else {
@@ -1576,30 +1640,52 @@ function jobsMatchingNames(octokit, owner, repo, runId, jobNameRegexps, checkIfF
         return false;
     });
 }
-function getWorkflowId(octokit, runId, owner, repo) {
+/**
+ * Retrieves workflowId from the workflow URL.
+ * @param workflowUrl workflow URL to retrieve the ID from
+ * @return numerical workflow id
+ */
+function retrieveWorkflowIdFromUrl(workflowUrl) {
+    const workflowIdString = workflowUrl.split('/').pop() || '';
+    if (!(workflowIdString.length > 0)) {
+        throw new Error('Could not resolve workflow');
+    }
+    return parseInt(workflowIdString);
+}
+/**
+ * Returns workflowId of the runId specified
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @return workflow ID for the run id
+ */
+function getWorkflowId(repositoryInfo, runId) {
     return __awaiter(this, void 0, void 0, function* () {
-        const reply = yield octokit.actions.getWorkflowRun({
-            owner,
-            repo,
+        const reply = yield repositoryInfo.octokit.actions.getWorkflowRun({
+            owner: repositoryInfo.owner,
+            repo: repositoryInfo.repo,
             // eslint-disable-next-line @typescript-eslint/camelcase
             run_id: runId
         });
         core.info(`The source run ${runId} is in ${reply.data.workflow_url} workflow`);
-        const workflowIdString = reply.data.workflow_url.split('/').pop() || '';
-        if (!(workflowIdString.length > 0)) {
-            throw new Error('Could not resolve workflow');
-        }
-        return parseInt(workflowIdString);
+        return retrieveWorkflowIdFromUrl(reply.data.workflow_url);
     });
 }
-function getWorkflowRuns(octokit, statusValues, cancelMode, createListRunQuery) {
+/**
+ * Returns workflow runs matching the callable adding query criteria
+ * @param repositoryInfo - information about the repository used
+ * @param statusValues - array of string status values for runs that we are interested at
+ * @param cancelMode - which cancel mode the query is about
+ * @param createListRunQuery - what is the callable criteria selection
+ * @return map of workflow run items indexed by the workflow run number
+ */
+function getWorkflowRuns(repositoryInfo, statusValues, cancelMode, createListRunQuery) {
     var e_2, _a;
     return __awaiter(this, void 0, void 0, function* () {
-        const workflowRuns = new treemap.TreeMap();
+        const workflowRuns = new Map();
         for (const status of statusValues) {
             const listRuns = yield createListRunQuery(status);
             try {
-                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                for (var _b = __asyncValues(repositoryInfo.octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
                     const item = _c.value;
                     // There is some sort of bug where the pagination URLs point to a
                     // different endpoint URL which trips up the resulting representation
@@ -1622,14 +1708,114 @@ function getWorkflowRuns(octokit, statusValues, cancelMode, createListRunQuery)
         return workflowRuns;
     });
 }
-function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNamesRegexps, skipEventTypes) {
+/**
+ * True if the request is candidate for cancelling in case of duplicate deletion
+ * @param runItem item to check
+ * @param headRepo head Repo that we are checking against
+ * @param cancelFutureDuplicates whether future duplicates are being cancelled
+ * @param sourceRunId the source Run Id that originates the request
+ * @return true if we determine that the run Id should be cancelled
+ */
+function isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId) {
+    const runHeadRepo = runItem.head_repository.full_name;
+    if (headRepo !== undefined && runHeadRepo !== headRepo) {
+        core.info(`\nThe run ${runItem.id} is from a different ` +
+            `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
+        return false;
+    }
+    if (cancelFutureDuplicates) {
+        core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
+        return true;
+    }
+    else {
+        if (runItem.id === sourceRunId) {
+            core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`);
+            return false;
+        }
+        else if (runItem.id > sourceRunId) {
+            core.info(`\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`);
+            return false;
+        }
+        core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`);
+        return true;
+    }
+}
+/**
+ * Should the run is candidate for cancelling in SELF cancelling mode?
+ * @param runItem run item
+ * @param sourceRunId source run id
+ * @return true if the run item is self
+ */
+function isCandidateForCancellingSelf(runItem, sourceRunId) {
+    if (runItem.id === sourceRunId) {
+        core.info(`\nReturning the "source" run: ${runItem.id}.\n`);
+        return true;
+    }
+    else {
+        return false;
+    }
+}
+/**
+ * Should the run is candidate for cancelling in naming job cancelling mode?
+ * @param repositoryInfo - information about the repository used
+ * @param runItem run item
+ * @param jobNamesRegexps array of regexps to match job names against
+ * @return true if the run item contains jobs with names matching the pattern
+ */
+function isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+    return __awaiter(this, void 0, void 0, function* () {
+        // Cancel all jobs that have failed jobs (no matter when started)
+        if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)) {
+            core.info(`\nSome jobs have matching names in ${runItem.id} . Returning it.\n`);
+            return true;
+        }
+        else {
+            core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`);
+            return false;
+        }
+    });
+}
+/**
+ * Should the run is candidate for cancelling in failed job cancelling mode?
+ * @param repositoryInfo - information about the repository used
+ * @param runItem run item
+ * @param jobNamesRegexps array of regexps to match job names against
+ * @return true if the run item contains failed jobs with names matching the pattern
+ */
+function isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+    return __awaiter(this, void 0, void 0, function* () {
+        // Cancel all jobs that have failed jobs (no matter when started)
+        if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)) {
+            core.info(`\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`);
+            return true;
+        }
+        else {
+            core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`);
+            return false;
+        }
+    });
+}
+/**
+ * Determines whether the run is candidate to be cancelled depending on the mode used
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param headRepo - head repository
+ * @param cancelMode - cancel mode
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param sourceRunId - what is the source run id
+ * @param jobNamesRegexps - what are the regexps for job names
+ * @param skipEventTypes - which events should be skipped
+ * @return true if the run id is candidate for cancelling
+ */
+function isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNamesRegexps, skipEventTypes) {
     return __awaiter(this, void 0, void 0, function* () {
         if ('completed' === runItem.status.toString()) {
             core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
             return false;
         }
-        if (!CANCELLABLE_RUNS.includes(runItem.event.toString())) {
-            core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not in ${CANCELLABLE_RUNS}). Not cancelling it.\n`);
+        if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
+            core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not ` +
+                `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`);
             return false;
         }
         if (skipEventTypes.includes(runItem.event.toString())) {
@@ -1638,72 +1824,38 @@ function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode,
             return false;
         }
         if (cancelMode === CancelMode.FAILED_JOBS) {
-            // Cancel all jobs that have failed jobs (no matter when started)
-            if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, true)) {
-                core.info(`\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`);
-                return true;
-            }
-            else {
-                core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`);
-                return false;
-            }
+            return yield isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps);
         }
         else if (cancelMode === CancelMode.NAMED_JOBS) {
-            // Cancel all jobs that have failed jobs (no matter when started)
-            if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, false)) {
-                core.info(`\nSome jobs have matching names in ${runItem.id} . Returning it.\n`);
-                return true;
-            }
-            else {
-                core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`);
-                return false;
-            }
+            return yield isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps);
         }
         else if (cancelMode === CancelMode.SELF) {
-            if (runItem.id === sourceRunId) {
-                core.info(`\nReturning the "source" run: ${runItem.id}.\n`);
-                return true;
-            }
-            else {
-                return false;
-            }
+            return isCandidateForCancellingSelf(runItem, sourceRunId);
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
-            const runHeadRepo = runItem.head_repository.full_name;
-            if (headRepo !== undefined && runHeadRepo !== headRepo) {
-                core.info(`\nThe run ${runItem.id} is from a different ` +
-                    `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
-                return false;
-            }
-            if (cancelFutureDuplicates) {
-                core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
-                return true;
-            }
-            else {
-                if (runItem.id === sourceRunId) {
-                    core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`);
-                    return false;
-                }
-                else if (runItem.id > sourceRunId) {
-                    core.info(`\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`);
-                    return false;
-                }
-                core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`);
-                return true;
-            }
+            return isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId);
+        }
+        else if (cancelMode === CancelMode.ALL_DUPLICATES) {
+            core.info(`Returning candidate ${runItem.id} for "all_duplicates" cancelling.`);
+            return true;
         }
         else {
             throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
         }
     });
 }
-function cancelRun(octokit, owner, repo, runId) {
+/**
+ * Cancels the specified workflow run.
+ * @param repositoryInfo - information about the repository used
+ * @param runId - run Id to cancel
+ */
+function cancelRun(repositoryInfo, runId) {
     return __awaiter(this, void 0, void 0, function* () {
         let reply;
         try {
-            reply = yield octokit.actions.cancelWorkflowRun({
-                owner,
-                repo,
+            reply = yield repositoryInfo.octokit.actions.cancelWorkflowRun({
+                owner: repositoryInfo.owner,
+                repo: repositoryInfo.repo,
                 // eslint-disable-next-line @typescript-eslint/camelcase
                 run_id: runId
             });
@@ -1714,122 +1866,288 @@ function cancelRun(octokit, owner, repo, runId) {
         }
     });
 }
-function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+/**
+ * Returns map of workflow run items matching the criteria specified group by workflow run id
+ * @param repositoryInfo - information about the repository used
+ * @param statusValues - status values we want to check
+ * @param cancelMode - cancel mode to use
+ * @param sourceWorkflowId - source workflow id
+ * @param sourceRunId - source run id
+ * @param sourceEventName - name of the source event
+ * @param mySourceGroup - source of the run that originated it
+ * @return map of the run items matching grouped by workflow run id
+ */
+function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup) {
     return __awaiter(this, void 0, void 0, function* () {
-        const statusValues = ['queued', 'in_progress'];
-        const workflowRuns = yield getWorkflowRuns(octokit, statusValues, cancelMode, function (status) {
+        return yield getWorkflowRuns(repositoryInfo, statusValues, cancelMode, function (status) {
             if (cancelMode === CancelMode.SELF) {
-                core.info(`\nFinding runs for my own run: Owner: ${owner}, Repo: ${repo}, ` +
+                core.info(`\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
                     `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`);
-                return createListRunsQueryMyOwnRun(octokit, owner, repo, status, sourceWorkflowId, sourceRunId);
+                return createListRunsQuerySpecificRunId(repositoryInfo, status, sourceWorkflowId, sourceRunId);
             }
             else if (cancelMode === CancelMode.FAILED_JOBS ||
-                cancelMode === CancelMode.NAMED_JOBS) {
-                core.info(`\nFinding runs for all runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
-                    `Workflow ID:${sourceWorkflowId}\n`);
-                return createListRunsQueryAllRuns(octokit, owner, repo, status, sourceWorkflowId);
+                cancelMode === CancelMode.NAMED_JOBS ||
+                cancelMode === CancelMode.ALL_DUPLICATES) {
+                core.info(`\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
+                    `Status: ${status} Workflow ID:${sourceWorkflowId}\n`);
+                return createListRunsQueryAllRuns(repositoryInfo, status, sourceWorkflowId);
             }
             else if (cancelMode === CancelMode.DUPLICATES) {
-                core.info(`\nFinding duplicate runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
-                    `Workflow ID:${sourceWorkflowId}, Head Branch: ${headBranch},` +
+                core.info(`\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
+                    `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
                     `Event name: ${sourceEventName}\n`);
-                return createListRunsQueryOtherRuns(octokit, owner, repo, status, sourceWorkflowId, headBranch, sourceEventName);
+                return createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup);
             }
             else {
                 throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
             }
         });
-        const workflowsToCancel = [];
-        const pullRequestToNotify = [];
+    });
+}
+/**
+ * Finds pull request matching its headRepo, headBranch and headSha
+ * @param repositoryInfo - information about the repository used
+ * @param headRepo - head repository from which Pull Request comes
+ * @param headBranch - head branch from which Pull Request comes
+ * @param headSha - sha for the head of the incoming Pull request
+ */
+function findPullRequest(repositoryInfo, headRepo, headBranch, headSha) {
+    return __awaiter(this, void 0, void 0, function* () {
+        // Finds Pull request for this workflow run
+        core.info(`\nFinding PR request id for: owner: ${repositoryInfo.owner}, Repo:${repositoryInfo.repo},` +
+            ` Head:${headRepo}:${headBranch}.\n`);
+        const pullRequests = yield repositoryInfo.octokit.pulls.list({
+            owner: repositoryInfo.owner,
+            repo: repositoryInfo.repo,
+            head: `${headRepo}:${headBranch}`
+        });
+        for (const pullRequest of pullRequests.data) {
+            core.info(`\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`);
+            if (pullRequest.head.sha === headSha) {
+                core.info(`\nFound PR: ${pullRequest.number}\n`);
+                return pullRequest;
+            }
+        }
+        core.info(`\nCould not find the PR for this build :(\n`);
+        return null;
+    });
+}
+/**
+ * Finds pull request id for the run item.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run Item that the pull request should be found for
+ * @return pull request number to notify (or undefined if not found)
+ */
+function findPullRequestForRunItem(repositoryInfo, runItem) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const pullRequest = yield findPullRequest(repositoryInfo, runItem.head_repository.owner.login, runItem.head_branch, runItem.head_sha);
+        if (pullRequest) {
+            return pullRequest.number;
+        }
+        return undefined;
+    });
+}
+/**
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
+ * same group are put together in one array - in a map indexed by the source group id.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param headRepo - head repository the event comes from
+ * @param cancelMode - cancel mode to use
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param sourceRunId - source run id for the run
+ * @param jobNameRegexps - regexps for job names
+ * @param skipEventTypes - array of event names to skip
+ * @param workflowRuns - map of workflow runs found
+ * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ */
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const mapOfWorkflowRunCandidates = new Map();
         for (const [key, runItem] of workflowRuns) {
             core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
                 ` Created at ${runItem.created_at}\n`);
-            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes)) {
-                if (notifyPRCancel && runItem.event === 'pull_request') {
-                    const pullRequest = yield findPullRequest(octokit, owner, repo, runItem.head_repository.owner.login, runItem.head_branch, runItem.head_sha);
-                    if (pullRequest) {
-                        pullRequestToNotify.push(pullRequest.number);
-                    }
+            if (yield isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes)) {
+                const candidateSourceGroup = {
+                    workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
+                    headBranch: runItem.head_branch,
+                    headRepo: runItem.head_repository.full_name,
+                    eventName: runItem.event
+                };
+                const sourceGroupId = getSourceGroupId(candidateSourceGroup);
+                let workflowRunArray = mapOfWorkflowRunCandidates.get(sourceGroupId);
+                if (workflowRunArray === undefined) {
+                    workflowRunArray = [];
+                    mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray);
                 }
-                workflowsToCancel.push([runItem.id, runItem.created_at]);
-            }
-        }
-        // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
-        const sortedRunTuplesToCancel = workflowsToCancel.sort((runTuple1, runTuple2) => runTuple2[1].localeCompare(runTuple1[1]));
-        if (sortedRunTuplesToCancel.length > 0) {
-            if (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) {
-                core.info(`\nSkipping the first run (${sortedRunTuplesToCancel[0]}) of all the matching ` +
-                    `duplicates - this one we are going to leave in peace!\n`);
-                sortedRunTuplesToCancel.shift();
-            }
-            if (sortedRunTuplesToCancel.length === 0) {
-                core.info(`\nNo duplicates to cancel!\n`);
-                return sortedRunTuplesToCancel.map(runTuple => runTuple[0]);
-            }
-            core.info('\n######  Cancelling runs starting from the oldest  ##########\n' +
-                `\n     Runs to cancel: ${sortedRunTuplesToCancel.length}\n` +
-                `\n     PRs to notify: ${pullRequestToNotify.length}\n`);
-            for (const runTuple of sortedRunTuplesToCancel) {
-                core.info(`\nCancelling run: ${runTuple}.\n`);
-                yield cancelRun(octokit, owner, repo, runTuple[0]);
-            }
-            for (const pullRequestNumber of pullRequestToNotify) {
-                const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
-                yield addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`);
+                core.info(`The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`);
+                workflowRunArray.push(runItem);
             }
-            core.info('\n######  Finished cancelling runs                  ##########\n');
         }
-        else {
-            core.info('\n######  There are no runs to cancel!              ##########\n');
-        }
-        return sortedRunTuplesToCancel.map(runTuple => runTuple[0]);
+        return mapOfWorkflowRunCandidates;
     });
 }
-function getRequiredEnv(key) {
-    const value = process.env[key];
-    if (value === undefined) {
-        const message = `${key} was not defined.`;
-        throw new Error(message);
-    }
-    return value;
-}
-function addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, comment) {
+/**
+ * Add specified comment to Pull Request
+ * @param repositoryInfo - information about the repository used
+ * @param pullRequestNumber - number of pull request
+ * @param comment - comment to add
+ */
+function addCommentToPullRequest(repositoryInfo, pullRequestNumber, comment) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info(`\nNotifying PR: ${pullRequestNumber} with '${comment}'.\n`);
-        yield octokit.issues.createComment({
-            owner,
-            repo,
+        yield repositoryInfo.octokit.issues.createComment({
+            owner: repositoryInfo.owner,
+            repo: repositoryInfo.repo,
             // eslint-disable-next-line @typescript-eslint/camelcase
             issue_number: pullRequestNumber,
             body: comment
         });
     });
 }
-function findPullRequest(octokit, owner, repo, headRepo, headBranch, headSha) {
+/**
+ * Notifies PR about cancelling
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - my own run id
+ * @param pullRequestNumber - number of pull request
+ * @param reason reason for canceling
+ */
+function notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason) {
     return __awaiter(this, void 0, void 0, function* () {
-        // Finds Pull request for this workflow run
-        core.info(`\nFinding PR request id for: owner: ${owner}, Repo:${repo}, Head:${headRepo}:${headBranch}.\n`);
-        const pullRequests = yield octokit.pulls.list({
-            owner,
-            repo,
-            head: `${headRepo}:${headBranch}`
-        });
-        for (const pullRequest of pullRequests.data) {
-            core.info(`\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`);
-            if (pullRequest.head.sha === headSha) {
-                core.info(`\nFound PR: ${pullRequest.number}\n`);
-                return pullRequest;
+        const selfWorkflowRunUrl = `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
+            `/actions/runs/${selfRunId}`;
+        yield addCommentToPullRequest(repositoryInfo, pullRequestNumber, `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`);
+    });
+}
+/**
+ * Cancels all runs in the specified group of runs.
+ * @param repositoryInfo - information about the repository used
+ * @param sortedRunItems - items sorted in descending order (descending order by created_at)
+ * @param notifyPRCancel - whether to notify the PR when cancelling
+ * @param selfRunId - what is the self run id
+ * @param sourceGroupId - what is the source group id
+ * @param reason - reason for canceling
+ */
+function cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info(`\n###### Cancelling runs for ${sourceGroupId} starting from the most recent  ##########\n` +
+            `\n     Number of runs to cancel: ${sortedRunItems.length}\n`);
+        const cancelledRuns = [];
+        for (const runItem of sortedRunItems) {
+            if (notifyPRCancel && runItem.event === 'pull_request') {
+                const pullRequestNumber = yield findPullRequestForRunItem(repositoryInfo, runItem);
+                if (pullRequestNumber !== undefined) {
+                    core.info(`\nNotifying PR: ${pullRequestNumber} (runItem: ${runItem}) with: ${reason}\n`);
+                    yield notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason);
+                }
             }
+            core.info(`\nCancelling run: ${runItem}.\n`);
+            yield cancelRun(repositoryInfo, runItem.id);
+            cancelledRuns.push(runItem.id);
         }
-        core.info(`\nCould not find the PR for this build :(\n`);
-        return null;
+        core.info(`\n######  Finished cancelling runs for ${sourceGroupId} ##########\n`);
+        return cancelledRuns;
     });
 }
-function getOrigin(octokit, runId, owner, repo) {
+/**
+ * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
+ * descending according to create date in each of the source groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicates.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param cancelMode - cancel mode
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param notifyPRCancel - whether to notify PRs with comments
+ * @param selfRunId - self run Id
+ * @param reason - reason for canceling
+ */
+function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason) {
     return __awaiter(this, void 0, void 0, function* () {
-        const reply = yield octokit.actions.getWorkflowRun({
-            owner,
-            repo,
+        const cancelledRuns = [];
+        for (const [sourceGroupId, candidatesArray] of mapOfWorkflowRunsCandidatesToCancel) {
+            // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
+            const sortedRunItems = candidatesArray.sort((runItem1, runItem2) => runItem2.created_at.localeCompare(runItem1.created_at));
+            if (sortedRunItems.length > 0) {
+                if ((cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
+                    cancelMode === CancelMode.ALL_DUPLICATES) {
+                    core.info(`\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
+                        `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`);
+                    sortedRunItems.shift();
+                }
+                if (sortedRunItems.length === 0) {
+                    core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`);
+                    continue;
+                }
+                cancelledRuns.push(...(yield cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason)));
+            }
+            else {
+                core.info(`\n######  There are no runs to cancel for ${sourceGroupId} ##########\n`);
+            }
+        }
+        return cancelledRuns;
+    });
+}
+/**
+ * Find and cancels runs based on the criteria chosen.
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - source workflow id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param sourceRunId - source run id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param headRepo - head repository that triggered the workflow (repo from which PR came)
+ * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
+ * @param sourceEventName - name of the event that triggered the workflow
+ *        (different than self event name for workflow_run)
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param notifyPRCancel - whether to notify in PRs about cancelling
+ * @param notifyPRMessageStart - whether to notify PRs when the action starts
+ * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
+ * @param skipEventTypes - array of event names that should be skipped
+ * @param reason - reason for cancelling
+ * @return array of canceled workflow run ids
+ */
+function findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const statusValues = ['queued', 'in_progress'];
+        const mySourceGroup = {
+            headBranch,
+            headRepo,
+            eventName: sourceEventName,
+            workflowId: sourceWorkflowId
+        };
+        const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup);
+        const mapOfWorkflowRunsCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns);
+        return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
+    });
+}
+/**
+ * Returns environment variable that is required - throws error if it is not defined.
+ * @param key key for the env variable
+ * @return value of the env variable
+ */
+function getRequiredEnv(key) {
+    const value = process.env[key];
+    if (value === undefined) {
+        const message = `${key} was not defined.`;
+        throw new Error(message);
+    }
+    return value;
+}
+/**
+ * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * @param repositoryInfo - information about the repository used
+ * @param runId - run id of the run to check
+ * @return information about the triggering workflow
+ */
+function getOrigin(repositoryInfo, runId) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const reply = yield repositoryInfo.octokit.actions.getWorkflowRun({
+            owner: repositoryInfo.owner,
+            repo: repositoryInfo.repo,
             // eslint-disable-next-line @typescript-eslint/camelcase
             run_id: runId
         });
@@ -1839,22 +2157,44 @@ function getOrigin(octokit, runId, owner, repo) {
             `Event: ${sourceRun.event}, Head sha: ${sourceRun.head_sha}, url: ${sourceRun.url}`);
         let pullRequest = null;
         if (sourceRun.event === 'pull_request') {
-            pullRequest = yield findPullRequest(octokit, owner, repo, sourceRun.head_repository.owner.login, sourceRun.head_branch, sourceRun.head_sha);
-        }
-        return [
-            reply.data.head_repository.full_name,
-            reply.data.head_branch,
-            reply.data.event,
-            reply.data.head_sha,
-            pullRequest ? pullRequest.merge_commit_sha : '',
-            pullRequest
-        ];
+            pullRequest = yield findPullRequest(repositoryInfo, sourceRun.head_repository.owner.login, sourceRun.head_branch, sourceRun.head_sha);
+        }
+        return {
+            headRepo: reply.data.head_repository.full_name,
+            headBranch: reply.data.head_branch,
+            headSha: reply.data.head_sha,
+            mergeCommitSha: pullRequest ? pullRequest.merge_commit_sha : null,
+            pullRequest: pullRequest ? pullRequest : null,
+            eventName: reply.data.event
+        };
     });
 }
-function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
+/**
+ * Performs the actual cancelling.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - source workflow id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param sourceRunId - source run id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param headRepo - head repository that triggered the workflow (repo from which PR came)
+ * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
+ * @param sourceEventName - name of the event that triggered the workflow
+ *        (different than self event name for workflow_run)
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param notifyPRCancel - whether to notify in PRs about cancelling
+ * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
+ *        generated automatically)
+ * @param notifyPRMessageStart - whether to notify PRs when the action starts
+ * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
+ * @param skipEventTypes - array of event names that should be skipped
+ */
+function performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
-        core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
+        core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
             `head repo ${headRepo}, headBranch: ${headBranch}, ` +
             `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
         core.info('\n###################################################################################\n');
@@ -1874,20 +2214,139 @@ function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, own
             reason = `It has jobs matching ${jobNameRegexps}.`;
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
-            core.info(`# Cancel duplicate runs started before ${sourceRunId} for workflow ${sourceWorkflowId}.`);
-            reason = `It in earlier duplicate of ${sourceWorkflowId} run.`;
+            core.info(`# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`);
+            reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
+        }
+        else if (cancelMode === CancelMode.ALL_DUPLICATES) {
+            core.info(`# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`);
+            reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
         }
         else {
             throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+        return yield findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
     });
 }
+/**
+ * Retrieves information about source workflow Id. Either from the current event or from the workflow
+ * nme specified. If the file name is not specified, the workflow to act on is either set to self event
+ * or in case of workflow_run event - to the workflow id that triggered the 'workflow_run' event.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param workflowFileName - optional workflow file name
+ * @param sourceRunId - source run id of the workfow
+ * @param selfRunId - self run id
+ * @return workflow id that is associate with the workflow we are going to act on.
+ */
+function retrieveWorkflowId(repositoryInfo, workflowFileName, sourceRunId, selfRunId) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let sourceWorkflowId;
+        if (workflowFileName) {
+            sourceWorkflowId = workflowFileName;
+            core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`);
+        }
+        else {
+            sourceWorkflowId = yield getWorkflowId(repositoryInfo, sourceRunId);
+            if (sourceRunId === selfRunId) {
+                core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
+            }
+            else {
+                core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
+            }
+        }
+        return sourceWorkflowId;
+    });
+}
+/**
+ * Sets output but also prints the output value in the logs.
+ *
+ * @param name name of the output
+ * @param value value of the output
+ */
 function verboseOutput(name, value) {
     core.info(`Setting output: ${name}: ${value}`);
     core.setOutput(name, value);
 }
+/**
+ * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
+ * are verified and in case od unexpected combination found, approrpriate error is raised.
+ *
+ * @param eventName - name of the event to act on
+ * @param sourceRunId - run id of the triggering event
+ * @param selfRunId - our own run id
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
+ * @param jobNameRegexps - array of regular expression of job names
+ */
+function performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps) {
+    if (eventName === 'workflow_run' &&
+        sourceRunId === selfRunId &&
+        cancelMode === CancelMode.DUPLICATES) {
+        throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
+            'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
+            'See the docs for details.');
+    }
+    if (jobNameRegexps.length > 0 &&
+        [
+            CancelMode.DUPLICATES,
+            CancelMode.SELF,
+            CancelMode.ALL_DUPLICATES
+        ].includes(cancelMode)) {
+        throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
+    }
+    if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+        throw Error(`The  ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`);
+    }
+}
+/**
+ * Produces basic outputs for the action. This does not include cancelled workflow run id - those are
+ * set after cancelling is done.
+ *
+ * @param triggeringWorkflowInfo
+ */
+function produceBasicOutputs(triggeringWorkflowInfo) {
+    verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo);
+    verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch);
+    verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha);
+    verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName);
+    verboseOutput('pullRequestNumber', triggeringWorkflowInfo.pullRequest
+        ? triggeringWorkflowInfo.pullRequest.number.toString()
+        : '');
+    verboseOutput('mergeCommitSha', triggeringWorkflowInfo.mergeCommitSha
+        ? triggeringWorkflowInfo.mergeCommitSha
+        : '');
+    verboseOutput('targetCommitSha', triggeringWorkflowInfo.mergeCommitSha
+        ? triggeringWorkflowInfo.mergeCommitSha
+        : triggeringWorkflowInfo.headSha);
+}
+/**
+ * Notifies the PR that the action has started.
+ *
+ * @param repositoryInfo information about the repository
+ * @param triggeringWorkflowInfo information about the triggering workflow
+ * @param sourceRunId run id of the source workflow
+ * @param selfRunId self run id
+ * @param notifyPRMessageStart whether to notify about the start of the action
+ */
+function notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+            const selfWorkflowRunUrl = `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
+                `/actions/runs/${selfRunId}`;
+            yield repositoryInfo.octokit.issues.createComment({
+                owner: repositoryInfo.owner,
+                repo: repositoryInfo.repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                issue_number: triggeringWorkflowInfo.pullRequest.number,
+                body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
+            });
+        }
+    });
+}
+/**
+ * Main run method that does everything :)
+ */
 function run() {
     return __awaiter(this, void 0, void 0, function* () {
         const token = core.getInput('token', { required: true });
@@ -1911,55 +2370,23 @@ function run() {
             : [];
         const workflowFileName = core.getInput('workflowFileName');
         const [owner, repo] = repository.split('/');
+        const repositoryInfo = {
+            octokit,
+            owner,
+            repo
+        };
         core.info(`\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
             ` skipEventTypes: ${skipEventTypes}\n`);
-        let sourceWorkflowId;
-        if (workflowFileName) {
-            sourceWorkflowId = workflowFileName;
-            core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`);
-        }
-        else {
-            sourceWorkflowId = yield getWorkflowId(octokit, sourceRunId, owner, repo);
-            if (sourceRunId === selfRunId) {
-                core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
-            }
-            else {
-                core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
-            }
-            if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
-                if (cancelMode === CancelMode.DUPLICATES)
-                    throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
-                        'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
-                        'See the docs for details.');
-            }
-        }
+        const sourceWorkflowId = yield retrieveWorkflowId(repositoryInfo, workflowFileName, sourceRunId, selfRunId);
+        performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps);
         core.info(`Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
             `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
             `sourceWorkflowId: ${sourceWorkflowId}, sourceRunId: ${sourceRunId}, selfRunId: ${selfRunId}, ` +
             `jobNames: ${jobNameRegexps}`);
-        if (jobNameRegexps.length > 0 &&
-            [CancelMode.DUPLICATES, CancelMode.SELF].includes(cancelMode)) {
-            throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
-        }
-        const [headRepo, headBranch, sourceEventName, headSha, mergeCommitSha, pullRequest] = yield getOrigin(octokit, sourceRunId, owner, repo);
-        verboseOutput('sourceHeadRepo', headRepo);
-        verboseOutput('sourceHeadBranch', headBranch);
-        verboseOutput('sourceHeadSha', headSha);
-        verboseOutput('sourceEvent', sourceEventName);
-        verboseOutput('pullRequestNumber', pullRequest ? pullRequest.number.toString() : '');
-        verboseOutput('mergeCommitSha', mergeCommitSha);
-        verboseOutput('targetCommitSha', pullRequest ? mergeCommitSha : headSha);
-        const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
-        if (notifyPRMessageStart && pullRequest) {
-            yield octokit.issues.createComment({
-                owner,
-                repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                issue_number: pullRequest.number,
-                body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
-            });
-        }
-        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
+        const triggeringWorkflowInfo = yield getOrigin(repositoryInfo, sourceRunId);
+        produceBasicOutputs(triggeringWorkflowInfo);
+        yield notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart);
+        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, triggeringWorkflowInfo.headRepo, triggeringWorkflowInfo.headBranch, triggeringWorkflowInfo.eventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
@@ -9118,3836 +9545,6 @@ module.exports = (promise, onFinally) => {
 
 /***/ }),
 
-/***/ 706:
-/***/ (function(module) {
-
-(function webpackUniversalModuleDefinition(root, factory) {
-	if(true)
-		module.exports = factory();
-	else { var i, a; }
-})(typeof self !== 'undefined' ? self : this, function() {
-return /******/ (function(modules) { // webpackBootstrap
-/******/ 	// The module cache
-/******/ 	var installedModules = {};
-/******/
-/******/ 	// The require function
-/******/ 	function __webpack_require__(moduleId) {
-/******/
-/******/ 		// Check if module is in cache
-/******/ 		if(installedModules[moduleId]) {
-/******/ 			return installedModules[moduleId].exports;
-/******/ 		}
-/******/ 		// Create a new module (and put it into the cache)
-/******/ 		var module = installedModules[moduleId] = {
-/******/ 			i: moduleId,
-/******/ 			l: false,
-/******/ 			exports: {}
-/******/ 		};
-/******/
-/******/ 		// Execute the module function
-/******/ 		modules[moduleId].call(module.exports, module, module.exports, __webpack_require__);
-/******/
-/******/ 		// Flag the module as loaded
-/******/ 		module.l = true;
-/******/
-/******/ 		// Return the exports of the module
-/******/ 		return module.exports;
-/******/ 	}
-/******/
-/******/
-/******/ 	// expose the modules object (__webpack_modules__)
-/******/ 	__webpack_require__.m = modules;
-/******/
-/******/ 	// expose the module cache
-/******/ 	__webpack_require__.c = installedModules;
-/******/
-/******/ 	// define getter function for harmony exports
-/******/ 	__webpack_require__.d = function(exports, name, getter) {
-/******/ 		if(!__webpack_require__.o(exports, name)) {
-/******/ 			Object.defineProperty(exports, name, {
-/******/ 				configurable: false,
-/******/ 				enumerable: true,
-/******/ 				get: getter
-/******/ 			});
-/******/ 		}
-/******/ 	};
-/******/
-/******/ 	// getDefaultExport function for compatibility with non-harmony modules
-/******/ 	__webpack_require__.n = function(module) {
-/******/ 		var getter = module && module.__esModule ?
-/******/ 			function getDefault() { return module['default']; } :
-/******/ 			function getModuleExports() { return module; };
-/******/ 		__webpack_require__.d(getter, 'a', getter);
-/******/ 		return getter;
-/******/ 	};
-/******/
-/******/ 	// Object.prototype.hasOwnProperty.call
-/******/ 	__webpack_require__.o = function(object, property) { return Object.prototype.hasOwnProperty.call(object, property); };
-/******/
-/******/ 	// __webpack_public_path__
-/******/ 	__webpack_require__.p = "";
-/******/
-/******/ 	// Load entry module and return exports
-/******/ 	return __webpack_require__(__webpack_require__.s = 5);
-/******/ })
-/************************************************************************/
-/******/ ([
-/* 0 */
-/***/ (function(module, exports, __nested_webpack_require_2850__) {
-
-"use strict";
-
-
-/**
- * @private
- */
-const RED = 1;
-/**
- * @private
- */
-const BLACK = 2;
-
-/**
- * @private
- * A node for a red-black tree
- */
-class TreeNode {
-
-    /**
-     * Default constructor
-     */
-    constructor() {
-        /** left child */
-        this.left = null;
-        /** right child */
-        this.right = null;
-        /** parent node */
-        this.parent = null;
-        /** key object (additional 'value' data member is added in map-like classes) */
-        this.key = null;
-        /** by default new node is red */
-        this.color = RED;
-    }
-
-    /**
-     * @returns parent of parent
-     */
-    grandparent() {
-        let p = this.parent;
-        if (p === null) {
-            return null;
-        } // No parent means no grandparent
-        return p.parent;
-    }
-
-    /**
-     * @returns the other child of the same parent
-     */
-    sibling() {
-        let p = this.parent;
-        if (p === null) {
-            return null;
-        } // No parent means no sibling
-        if (this === p.left) {
-            return p.right;
-        }
-        else {
-            return p.left;
-        }
-    }
-
-    /**
-     * @returns another child of the grandparent
-     */
-    uncle() {
-        let p = this.parent;
-        if (p === null) {
-            return null;
-        } // No parent means no uncle
-        let g = p.parent;
-        if (g === null) {
-            return null;
-        } // No grandparent means no uncle
-        return p.sibling();
-    }
-}
-
-module.exports = {
-    TreeNode: TreeNode,
-    BLACK: BLACK,
-    RED: RED
-};
-
-/***/ }),
-/* 1 */
-/***/ (function(module, exports) {
-
-/**
- * Used by sets
- * @private
- */
-class KeyOnlyPolicy {
-    /**
-     * Returns key data from the specified node
-     * @param {*} n
-     */
-    fetch(n) {
-        return n.key;
-    }
-
-    /**
-     * Copies key data from one node to another
-     * @param {*} dst
-     * @param {*} src
-     */
-    copy(dst, src) {
-        dst.key = src.key;
-    }
-
-    /**
-     * @returns string representation of the key
-     * @param {*} node
-     */
-    toString(node) {
-        return String(node.key);
-    }
-}
-
-/**
- * Used by maps
- * @private
- */
-class KeyValuePolicy {
-    /**
-     * Returns key-value data from the specified node
-     * @param {*} n
-     */
-    fetch(n) {
-        return [n.key, n.value];
-    }
-
-    /**
-     * Copies key-value data from one node to another
-     * @param {*} dst
-     * @param {*} src
-     */
-    copy(dst, src) {
-        dst.key = src.key;
-        dst.value = src.value;
-    }
-
-    /**
-     * @returns string representation of key-value pair
-     * @param {*} node
-     */
-    toString(node) {
-        return String(node.key) + ':' + String(node.value);
-    }
-}
-
-/**
- * Used for iteration through values of a map
- * @private
- */
-class ValueOnlyPolicy {
-    /**
-     * Returns data from the specified node
-     * @param {*} n
-     */
-    fetch(n) {
-        return n.value;
-    }
-
-    /**
-     * Copies value data from one node to another
-     * @param {*} dst
-     * @param {*} src
-     */
-    copy(dst, src) {
-        dst.value = src.value;
-    }
-
-    /**
-     * @returns string representation of node's value
-     * @param {*} node
-     */
-    toString(node) {
-        return String(node.value);
-    }
-}
-
-module.exports = {
-    KeyOnlyPolicy: KeyOnlyPolicy,
-    ValueOnlyPolicy: ValueOnlyPolicy,
-    KeyValuePolicy: KeyValuePolicy
-};
-
-/***/ }),
-/* 2 */
-/***/ (function(module, exports, __nested_webpack_require_6477__) {
-
-"use strict";
-
-
-/** @ignore */
-const {TreeNode, RED, BLACK} = __nested_webpack_require_6477__(0);
-/** @ignore */
-const {JsIterator, JsReverseIterator} = __nested_webpack_require_6477__(3);
-/** @ignore */
-const {Iterator, ReverseIterator} = __nested_webpack_require_6477__(4);
-/** @ignore */
-const {KeyOnlyPolicy, ValueOnlyPolicy, KeyValuePolicy} = __nested_webpack_require_6477__(1);
-/** @ignore */
-const {InsertionResult} = __nested_webpack_require_6477__(8);
-
-/** insertion mode of a multimap, nodes with the same keys can be added */
-const INSERT_MULTI = 1;
-/** if a node with the same key already exists then the subsequent attempts are ignored */
-const INSERT_UNIQUE = 2;
-/** if a node with the same key already exists then it's value is replaced on subsequent attempts */
-const INSERT_REPLACE = 3;
-
-/**
- * @private
- * Special node in a tree is created for performance reasons
- */
-class Head {
-    /** default constructor */
-    constructor() {
-        /** node with the smallest key */
-        this.leftmost = this;
-        /** node with the largest key */
-        this.rightmost = this;
-        /** root node of the tree */
-        this.root = this;
-        /** number of nodes in the tree */
-        this.size = 0;
-        /** extra tag used in debuggin of unit tests */
-        this.id = 'HEAD';
-    }
-}
-
-/**
- * @private
- * 3-way comparison, similar to strcmp and memcp in C programming language
- * @returns +1 if the value of rhs is greater than lhs
- *          -1 if the value of rhs is less than lhs
- *           0 if values are the same
- */
-function compare(lhs, rhs) {
-    if (lhs < rhs) {
-        return -1;
-    }
-    else if (lhs === rhs) {
-        return 0;
-    }
-    else {
-        return 1;
-    }
-}
-
-/**
- * Red-black tree
- * @access private
- */
-class Tree {
-    /** default constructor of an empty tree */
-    constructor() {
-        /** head */
-        this.head = new Head();
-        /** 3-way comparison function */
-        this.compare = compare;
-        /** must be an instance of KeyOnlyPolicy for sets, or KeyValuePolicy for maps */
-        this.valuePolicy = new KeyOnlyPolicy();
-    }
-
-    /**
-     * Deletes all nodes in the tree
-     */
-    clear() {
-        this.head = new Head();
-    }
-
-    /**
-     * @returns number of nodes in the tree
-     */
-    size() {
-        return this.head.size;
-    }
-
-    /**
-     * @private
-     * A wrapper that calls 3-way comparison of node keys
-     * @param {*} lhs
-     * @param {*} rhs
-     */
-    compareNodes(lhs, rhs) {
-        return this.compare(lhs.key, rhs.key);
-    }
-
-    /**
-     * @private
-     * used by rotation operations
-     */
-    replaceNode(oldNode, newNode) {
-        if (oldNode === newNode) {
-            return;
-        }
-        if (oldNode.parent === null) {
-            this.head.root = newNode;
-        }
-        else {
-            if (oldNode === oldNode.parent.left) {
-                oldNode.parent.left = newNode;
-            }
-            else {
-                oldNode.parent.right = newNode;
-            }
-        }
-
-        if (!this.isLeaf(newNode)) {
-            newNode.parent = oldNode.parent;
-        }
-    }
-
-    /**
-     * Rebalances tree as described below
-
-              X                                           Y
-             / \                                         / \
-            Y   c         right rotate -->              a   X
-           / \            <--  left rotate                 / \
-          a   b                                           b   c
-     * @private
-     */
-    rotateLeft(node) {
-        let right = node.right;
-        if (this.isLeaf(right)) {
-            throw new Error('rotateLeft can\'t be performed. The tree is corrupted');
-        }
-        this.replaceNode(node, right);
-
-        node.right = right.left;
-        if (right.left !== null) {
-            right.left.parent = node;
-        }
-
-        right.left = node;
-        node.parent = right;
-    }
-
-    /**
-     * Rebalances tree as described in rotateLeft
-     * @param {*} node - parent node
-     */
-    rotateRight(node) {
-        let left = node.left;
-        if (this.isLeaf(left)) {
-            throw new Error('rotateRight can\'t be performed. The tree is corrupted');
-        }
-        this.replaceNode(node, left);
-
-        node.left = left.right;
-        if (left.right !== null) {
-            left.right.parent = node;
-        }
-
-        left.right = node;
-        node.parent = left;
-    }
-
-    /**
-     * @returns true - for null pointers and head node; false - for all other nodes
-     * @param {*} node
-     */
-    isLeaf(node) {
-        if (node === null || node === this.head) {
-            return true;
-        }
-        return false;
-    }
-
-    /**
-     * Leaf nodes are considered 'black'. All real nodes contain 'color' data member
-     * @param {*} node
-     */
-    fetchColor(node) {
-        if (this.isLeaf(node)) {
-            return BLACK;
-        }
-        else {
-            return node.color;
-        }
-    }
-
-    /**
-     * Tests a node for 'blackness'.
-     * @param {*} node
-     */
-    isBlack(node) {
-        return (this.fetchColor(node) === BLACK);
-    }
-
-    /**
-     * Tests node for 'redness'.
-     * @param {*} node
-     */
-    isRed(node) {
-        return (this.fetchColor(node) === RED);
-    }
-
-    /* ===========================
-       INSERT
-       =========================== */
-    /**
-     * A node will be inserted into the tree even if nodes with the same key already exist
-     * @param {*} node
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     */
-    insertMulti(node) {
-        return this.insertNode(node, INSERT_MULTI);
-    }
-
-    /**
-     * The node is inserted into the tree only if nodes with the same key do not exist there
-     * @param {*} node
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     */
-    insertUnique(node) {
-        return this.insertNode(node, INSERT_UNIQUE);
-    }
-
-    /**
-     * The node is inserted. If a node with the same key exists it's value will be replaced by the value of the new node
-     * @param {*} node
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     */
-    insertOrReplace(node) {
-        return this.insertNode(node, INSERT_REPLACE);
-    }
-
-    /**
-     * @private
-     * Inserts node. Updates head node. Rebalances tree.
-     * @param {*} n - node
-     * @param {*} mode - one of INSERT_MULTI, INSERT_UNIQUE, INSERT_REPLACE
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     */
-    insertNode(n, mode = INSERT_MULTI) {
-        let res = this.insertNodeInternal(this.head.root, n, mode);
-        if (res.wasAdded) {
-            if (this.head.size === 0) {
-                this.head.root = n;
-                this.head.leftmost = n;
-                this.head.rightmost = n;
-
-                n.left = this.head;
-                n.right = this.head;
-            }
-            else if (this.head.leftmost.left === n) {
-                this.head.leftmost = n;
-                n.left = this.head;
-            }
-            else if (this.head.rightmost.right === n) {
-                this.head.rightmost = n;
-                n.right = this.head;
-            }
-            this.insertRepairTree(n);
-            this.head.size = this.head.size + 1;
-        }
-        return res;
-    }
-
-    /**
-     * @private
-     * Inserts node according to the mode
-     * @param {*} root - root node of the tree
-     * @param {*} n - node to be inserted
-     * @param {*} mode - one of INSERT_MULTI, INSERT_UNIQUE, INSERT_REPLACE
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     */
-    insertNodeInternal(root, n, mode) {
-        // recursively descend the tree until a leaf is found
-        let x = root;
-        let y = null;
-        let rc = -1;
-        // find matching node
-        while (!this.isLeaf(x)) {
-            y = x;
-            rc = this.compareNodes(n, y);
-            if (rc < 0) {
-                x = y.left;
-            }
-            else if (rc > 0) {
-                x = y.right;
-            }
-            else {
-                // node with the same key value
-                switch (mode) {
-                    case INSERT_UNIQUE:
-                        // it's a duplicate
-                        return new InsertionResult(false, false, undefined);
-                    case INSERT_REPLACE:
-                        this.valuePolicy.copy(y, n);
-                        return new InsertionResult(false, true, new Iterator(y, this));
-                    default:
-                        // INSERT_MULTI
-                        x = y.right;
-                }
-            }
-        }
-        if (this.isLeaf(y)) {
-            n.parent = null;
-            n.left = this.head;
-            n.right = this.head;
-        }
-        else {
-            n.parent = y;
-            if (rc < 0) {
-                y.left = n;
-            }
-            else {
-                y.right = n;
-            }
-        }
-        return new InsertionResult(true, false, new Iterator(n, this));
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
-     * @param {*} n - node
-     */
-    insertRepairTree(n) {
-        if (n.parent === null) {
-            this.repairCase1(n);
-        }
-        else if (this.isBlack(n.parent)) {
-        /* insert_case2(n);
-           // do nothing */
-        }
-        else if (this.isRed(n.uncle())) {
-            this.repairCase3(n);
-        }
-        else {
-            this.repairCase4(n);
-        }
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
-     * @param {*} n - node
-     */
-    repairCase1(n) {
-        n.color = BLACK;
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
-     * @param {*} n - node
-     */
-    repairCase3(n) {
-        n.parent.color = BLACK;
-        n.uncle().color = BLACK;
-        n.grandparent().color = RED;
-        this.insertRepairTree(n.grandparent());
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Insertion
-     * @param {*} n - node
-     */
-    repairCase4(n) {
-        let p = n.parent;
-        let g = n.grandparent();
-
-        let nr = null;
-        if ((g.left !== null)
-            && (n === g.left.right)) {
-            this.rotateLeft(p);
-            n = n.left;
-        }
-        else if ((g.right !== null)
-            && (n === g.right.left)) {
-            this.rotateRight(p);
-            n = n.right;
-        }
-
-        p = n.parent;
-        g = n.grandparent();
-        if (n === p.left) {
-            this.rotateRight(g);
-        }
-        else {
-            this.rotateLeft(g);
-        }
-
-        p.color = BLACK;
-        g.color = RED;
-    }
-
-    /**
-     * @returns the node with the highest key for the subtree of the specified root node
-     * @param {*} node - root node of the subtree to be evaluated
-     */
-    fetchMaximum(node) {
-        while (!this.isLeaf(node.right)) {
-            node = node.right;
-        }
-
-        return node;
-    }
-
-    /**
-     * @returns the node with the lowest key for the subtree of the specified root node
-     * @param {*} node - root node of the subtree to be evaluated
-     */
-    fetchMinimum(node) {
-        while (!this.isLeaf(node.left)) {
-            node = node.left;
-        }
-
-        return node;
-    }
-
-    /* ===========================
-       ERASE
-       =========================== */
-    /**
-     * Removes node from the tree
-     * @param {*} node
-     */
-    erase(node) {
-        if (this.isLeaf(node)) {
-            return;
-        }
-
-        this.eraseInternal(node);
-        let h = this.head;
-        h.size = h.size - 1;
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node - node
-     */
-    eraseInternal(node) {
-        if (!this.isLeaf(node.left)
-            && !this.isLeaf(node.right)) {
-            let pred = this.fetchMaximum(node.left);
-
-            this.valuePolicy.copy(node, pred);
-            node = pred;
-        }
-
-        let child = (this.isLeaf(node.right)) ? node.left : node.right;
-
-        if (this.isBlack(node)) {
-            this.eraseCase1(node);
-        }
-        this.replaceNode(node, child);
-        if (this.head.size === 2) {
-            if (!this.isLeaf(child)) {
-                // Root node must be BLACK
-                child.color = BLACK;
-            }
-        }
-
-        let h = this.head;
-        if (this.isLeaf(child)) {
-            /* The node didn't have children and it was removed
-               the head needs to update leftmost, rightmost pointers */
-            if (h.leftmost === node) {
-                let p = node.parent;
-                if (p !== null) {
-                    h.leftmost = p;
-                    p.left = h;
-                }
-                else {
-                    h.leftmost = h;
-                }
-            }
-            if (h.rightmost === node) {
-                let p = node.parent;
-                if (p !== null) {
-                    h.rightmost = p;
-                    p.right = h;
-                }
-                else {
-                    h.rightmost = h;
-                }
-            }
-        }
-        else {
-            // the node had a child. Now node is removed. Any references should point to the child now
-            if (h.leftmost === node) {
-                h.leftmost = child;
-                child.left = h;
-            }
-            if (h.rightmost === node) {
-                h.rightmost = child;
-                child.right = h;
-            }
-        }
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase1(node) {
-        if (node.parent === null) {
-            return;
-        }
-        else {
-            this.eraseCase2(node);
-        }
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase2(node) {
-        let s = node.sibling();
-
-        if (this.isRed(s)) {
-            node.parent.color = RED;
-            s.color = BLACK;
-
-            if (node === node.parent.left) {
-                this.rotateLeft(node.parent);
-            }
-            else {
-                this.rotateRight(node.parent);
-            }
-        }
-        this.eraseCase3(node);
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase3(node) {
-        let s = node.sibling();
-        let p = node.parent;
-        if (this.isBlack(p)
-            && this.isBlack(s)
-            && this.isBlack(s.left)
-            && this.isBlack(s.right)) {
-
-            s.color = RED;
-            this.eraseCase1(p);
-        }
-        else {
-            this.eraseCase4(node);
-        }
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase4(node) {
-        let s = node.sibling();
-        let p = node.parent;
-        if (this.isRed(p)
-            && this.isBlack(s)
-            && this.isBlack(s.left)
-            && this.isBlack(s.right)) {
-
-            s.color = RED;
-            p.color = BLACK;
-        }
-        else {
-            this.eraseCase5(node);
-        }
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase5(node) {
-        let s = node.sibling();
-        let p = node.parent;
-        /* The check below is unnecessary
-           due to case 2 (even though case 2 changed the sibling to a sibling's child,
-           the sibling's child can't be red, since no red parent can have a red child). */
-        /* if ((!this.isLeaf(s))
-               && this.isBlack(s)) { */
-
-        /* the following statements just force the red to be on the left of the left of the parent,
-           or right of the right, so case six will rotate correctly. */
-        if (node === p.left
-            && this.isRed(s.left)
-			&& this.isBlack(s.right)) {
-
-            s.color = RED;
-            s.left.color = BLACK;
-            this.rotateRight(s);
-        }
-        else if (node === p.right
-            && this.isBlack(s.left)
-            && this.isRed(s.right)) {
-
-            s.color = RED;
-            s.right.color = BLACK;
-            this.rotateLeft(s);
-        }
-        //}
-        this.eraseCase6(node);
-    }
-
-    /**
-     * @private
-     * The method is decribed at: https://en.wikipedia.org/wiki/Red%E2%80%93black_tree#Removal
-     * @param {*} node
-     */
-    eraseCase6(node) {
-        let s = node.sibling();
-        let p = node.parent;
-        s.color = this.fetchColor(p);
-        p.color = BLACK;
-
-        if (node === p.left) {
-            s.right.color = BLACK;
-            this.rotateLeft(p);
-        }
-        else {
-            s.left.color = BLACK;
-            this.rotateRight(p);
-        }
-    }
-
-    /* ===========================
-       SEARCH BY KEY
-       =========================== */
-    /**
-    * @returns an iterator pointin to a node with matching key value. If node is not found then end() iterator is returned.
-    * @param {*} k - key value
-    */
-    find(k) {
-        let y = this.head;
-        let x = y.root;
-        while (!this.isLeaf(x)) {
-            let rc = this.compare(x.key, k);
-            if (rc > 0) {
-                y = x;
-                x = x.left;
-            }
-            else if (rc < 0) {
-                y = x;
-                x = x.right;
-            }
-            else {
-                return new Iterator(x, this);
-            }
-        }
-        return new Iterator(this.head, this);
-    }
-
-    /**
-     * @returns an iterator pointing to the first node in the tree that is not less than
-     * (i.e. greater or equal to) the specified key value, or end() if no such node is found.
-     * @param {*} k - key value
-     */
-    lowerBound(k) {
-        let y = this.head;
-        let x = y.root;
-        while (!this.isLeaf(x)) {
-            let rc = this.compare(x.key, k);
-            if (rc >= 0) {
-                y = x;
-                x = x.left;
-            }
-            else {
-                x = x.right;
-            }
-        }
-        return new Iterator(y, this);
-    }
-
-    /**
-     * @returns an iterator pointing to the first node in the tree that is greater than
-     * the specified key value, or end() if no such node is found.
-     * @param {*} k - key value
-     */
-    upperBound(k) {
-        let y = this.head;
-        let x = y.root;
-        while (!this.isLeaf(x)) {
-            let rc = this.compare(x.key, k);
-            if (rc > 0) {
-                y = x;
-                x = x.left;
-            }
-            else {
-                x = x.right;
-            }
-        }
-        return new Iterator(y, this);
-    }
-
-    /* ===========================
-       ITERATORS
-       =========================== */
-
-    /**
-     * @returns iterator pointing to the node with the lowest key
-     */
-    begin() {
-        return new Iterator(this.head.leftmost, this);
-    }
-
-    /**
-     * @returns iterator pointing to the node following the node with the highest key
-     */
-    end() {
-        return new Iterator(this.head, this);
-    }
-
-    /**
-     * @returns iterator pointing to the node with the highest key
-     */
-    rbegin() {
-        return new ReverseIterator(this.head.rightmost, this);
-    }
-
-    /**
-     * @returns iterator pointing to the node preceding the node with the lowest key
-     */
-    rend() {
-        return new ReverseIterator(this.head, this);
-    }
-
-    /**
-     * @private
-     * provides support for ES6 forward iteration
-     */
-    jsBegin() {
-        return this.head.leftmost;
-    }
-
-    /**
-     * @private
-     * provides support for ES6 forward iteration
-     */
-    jsEnd() {
-        return this.head;
-    }
-
-    /**
-     * @private
-     * provides support for ES6 reverse iteration
-     */
-    jsRbegin() {
-        return this.head.rightmost;
-    }
-
-    /**
-     * @private
-     * provides support for ES6 forward iteration
-     */
-    jsRend() {
-        return this.head;
-    }
-
-    /**
-     * @returns node following the specified node in ascending order of their keys
-     * @param {*} n - node
-     */
-    next(n) {
-        if (n === this.head) {
-            return this.head.leftmost;
-        }
-        if (n.right === this.head) {
-            return this.head;
-        }
-        if (n.right !== null) {
-            let res = this.fetchMinimum(n.right);
-            return res;
-        }
-        else {
-            while (n.parent.left !== n) {
-                n = n.parent;
-            }
-            return n.parent;
-        }
-    }
-
-    /**
-     * @returns node preceding the specified node in ascending order of their keys
-     * @param {*} n - node
-     */
-    prev(n) {
-        if (n === this.head) {
-            return this.head.rightmost;
-        }
-        if (n.left === this.head) {
-            return this.head;
-        }
-        if (n.left !== null) {
-            let res = this.fetchMaximum(n.left);
-            return res;
-        }
-        else {
-            while (n.parent.right !== n) {
-                n = n.parent;
-            }
-            return n.parent;
-        }
-    }
-
-    /**
-     * ES6 forward iteration
-     */
-    [Symbol.iterator]() {
-        return new JsIterator(this);
-    }
-
-    /**
-     * ES6 reverse iteration
-     */
-    backward() {
-        return new JsReverseIterator(this);
-    }
-
-    /**
-     * @returns a new JsIterator object that contains the [key, value] pairs for each element in the order of the keys.
-     */
-    entries() {
-        return new JsIterator(this);
-    }
-
-    /**
-     * @returns a new JsIterator object that contains the keys for each element in the order of the keys.
-     */
-    keys() {
-        return new JsIterator(this, new KeyOnlyPolicy());
-    }
-
-    /**
-     * @returns a new JsIterator object that contains the values for each element in the order of the keys.
-     */
-    values() {
-        return new JsIterator(this, new ValueOnlyPolicy());
-    }
-
-    /**
-     * @returns first element of the container, or undefined if container is empty
-     */
-    first() {
-        if (this.size() === 0) {
-            return undefined;
-        }
-        else {
-            let it = this.begin();
-            return this.valuePolicy.fetch(it.node);
-        }
-    }
-
-    /**
-     * @returns last element of the container, or undefined if container is empty
-     */
-    last() {
-        if (this.size() === 0) {
-            return undefined;
-        }
-        else {
-            let it = this.rbegin();
-            return this.valuePolicy.fetch(it.node);
-        }
-    }
-
-    /**
-     * @returns String representation of the container
-     */
-    toString() {
-        let parts = [];
-        for (let it = this.begin(); !it.equals(this.end()); it.next()) {
-            // convert each key-value pair
-            parts.push(this.valuePolicy.toString(it.node));
-        }
-        return '{' + parts.join(',') + '}';
-    }
-
-    /**
-     * @returns String tag of this class
-     */
-    get [Symbol.toStringTag]() {
-        return 'Tree';
-    }
-
-    /**
-     * @returns constructor object for this class
-     */
-    static get [Symbol.species]() {
-        return Tree;
-    }
-
-}
-
-module.exports = {
-    Tree: Tree,
-    compare: compare
-};
-
-
-/***/ }),
-/* 3 */
-/***/ (function(module, exports, __nested_webpack_require_31214__) {
-
-"use strict";
-
-
-/* Containers are expected to support the following methods:
-   jsBegin() - returns the very first node
-   jsEnd() - returns the node beyond the last one
-   next(node) - returns the next node
-   prev(node) - returns the previous node
-   valuePolicy - an instance of KeyOnlyPolicy, or KeyValuePolicy */
-/**
-  * ES6-style forward iterator.
-  *
-  * @example
-  * let m = new TreeMap();
-  * ...
-  * for (let [key, value] of m) {
-  *   console.log(`key: ${key}, value: ${value}`);
-  * }
-  * // iterate values
-  * for (let value of m.values()) {
-  *   console.log(`value: ${value}`);
-  * }
-  */
-class JsIterator {
-    /**
-     * @param {*} container
-     */
-    constructor(container, valuePolicy = container.valuePolicy) {
-        /**
-         * @private
-         * Internal reference to a container
-         */
-        this.container = container;
-        /**
-         * @private
-         * valuePolicy implements what members of the node will be returned: key, value, or key and value
-         */
-        this.valuePolicy = valuePolicy;
-        /**
-         * @private
-         * current node
-         */
-        this.node = container.jsBegin();
-    }
-
-    /**
-     * As documented in ES6 iteration protocol. It can be used for manual iteration.
-     * Iterators are documented here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
-     *
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * let jsIt = m.entries();
-     * while (true) {
-     *   let res = it.next();
-     *   if (res.done) {
-     *     break;
-     *   }
-     *   console.log(`key: ${res.value[0]}, value: ${res.value[1]`});
-     * }
-     */
-    next() {
-        let res = {};
-        res.done = (this.node === this.container.jsEnd());
-        if (!res.done) {
-            res.value = this.valuePolicy.fetch(this.node);
-            this.node = this.container.next(this.node);
-        }
-        return res;
-    }
-
-    /**
-     * Support for ES6 for-of loops.
-     * @returns {JsIterator}
-     */
-    [Symbol.iterator]() {
-        return this;
-    }
-
-    /**
-     * A reverse iterator for the same container.
-     * @returns {JsReverseIterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * // iterate all key-value pairs in reverse order
-     * for (let [key, value] of m.backwards()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-    */
-    backwards() {
-        // eslint-disable-next-line no-use-before-define
-        return new JsReverseIterator(this.container, this.valuePolicy);
-    }
-}
-
-/* Containers are expected to support the following methods:
-   jsRbegin() - returns the very first node in reverse order (e.g. the very last node)
-   jsrEnd() - returns the node beyond the last one in reverse order (e.g. the node before the first one)
-   next(node) - returns the next node
-   prev(node) - returns the previous node
-   valuePolicy - an instance of KeyOnlyPolicy, or KeyValuePolicy */
-/**
-  * ES6-style backward iterator
-  * @example
-  * let m = new TreeMap();
-  * ...
-  * // iterate all key-value pairs in reverse order
-  * for (let [key, value] of m.backwards()) {
-  *   console.log(`key: ${key}, value: ${value}`);
-  * }
-  * // iterate keys in reverse order
-  * for (let key of m.keys().backwards()) {
-  *   console.log(`key: ${key}`);
-  * }
- */
-class JsReverseIterator {
-    /**
-     * @param {*} container
-     */
-    constructor(container, valuePolicy = container.valuePolicy) {
-        /**
-         * @private
-         * Internal reference to a container
-         */
-        this.container = container;
-        /**
-         * @private
-         * valuePolicy implements what members of the node will be returned: key, value, or key and value
-         */
-        this.valuePolicy = valuePolicy;
-        /**
-         * @private
-         * current node
-         */
-        this.node = container.jsRbegin();
-    }
-
-    /**
-     * As documented in ES6 iteration protocol. It can be used for manual iteration.
-     * Iterators are documented here: https://developer.mozilla.org/en-US/docs/Web/JavaScript/Guide/Iterators_and_Generators
-     *
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * let jsIt = m.entries().backwards();
-     * while (true) {
-     *   let res = it.next();
-     *   if (res.done) {
-     *     break;
-     *   }
-     *   console.log(`key: ${res.value[0]}, value: ${res.value[1]`});
-     * }
-     */
-    next() {
-        let res = {};
-        res.done = (this.node === this.container.jsRend());
-        if (!res.done) {
-            res.value = this.valuePolicy.fetch(this.node);
-            this.node = this.container.prev(this.node);
-        }
-        return res;
-    }
-
-    /**
-     * Support for ES6 for-of loops.
-     * @returns {JsReverseIterator}
-     */
-    [Symbol.iterator]() {
-        return this;
-    }
-
-    /**
-     * A forward iterator for the same container
-     * @returns {JsIterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * // iterate all key-value pairs in direct order
-     * for (let [key, value] of m.backwards().backwards()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     */
-    backwards() {
-        return new JsIterator(this.container, this.valuePolicy);
-    }
-}
-
-module.exports = {
-    JsIterator: JsIterator,
-    JsReverseIterator: JsReverseIterator
-};
-
-/***/ }),
-/* 4 */
-/***/ (function(module, exports, __nested_webpack_require_36806__) {
-
-"use strict";
-
-/**
- * Base class for STL-like iterators. It references a node (or index) and a container.
- * Navigation is achieved by calling container's prev() and next() methods.
- */
-class BaseIterator {
-    /**
-     * @param {*} node - current node
-     * @param {*} container - container
-     */
-    constructor(node, container) {
-        /**
-         * @private
-         * __n - internal node reference
-         */
-        this.__n = node;
-        /**
-         * @private
-         * __c - internal container reference
-         */
-        this.__c = container;
-    }
-
-    /**
-     * Two iterators are considered to be equal if they point to the same node of the same container
-     * @param {BaseIterator} rhs - object on the 'right-hand side' of .eq. operator
-     * @returns {boolean}
-     */
-    equals(rhs) {
-        let lhsClass = this.constructor.name;
-        let rhsClass = rhs.constructor.name;
-        if (lhsClass !== rhsClass) {
-            throw new Error(`Can't compare an instance of ${lhsClass} with an instance of ${rhsClass}`);
-        }
-        if (this.__c !== rhs.__c) {
-            throw new Error('Iterators belong to different containers');
-        }
-        return this.__n === rhs.__n;
-    }
-
-    /**
-     * @private
-     * @returns current node
-     */
-    get node() {
-        return this.__n;
-    }
-
-    /**
-     * @private
-     * @returns key of the current node
-     */
-    get key() {
-        return this.__n.key;
-    }
-
-    /**
-     * @private
-     * @returns value of the current node
-     */
-    get value() {
-        return this.__n.value;
-    }
-
-    /**
-     * @private
-     * @returns container that holds current node
-     */
-    get container() {
-        return this.__c;
-    }
-}
-
-/**
- * STL-like forward iterator. It's more verbose than ES6 iterators, but allows iteration over any part of the container
- *
- * @example
- * let m = new TreeMap();
- * ...
- * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
- *   console.log(`key: ${it.key}, value: ${it.value}`);
- * }
- */
-class Iterator extends BaseIterator {
-    /**
-     * There are 3 ways to construct an iterator:
-     *
-     * 1. Using a node and a container
-     * 2. Copy constructor / clone
-     * 3. Copy constructor / clone from ReverseIterator instance
-     * @param {*} args
-     *
-     * @example
-     * // Using a node and a container
-     * let it = new Iterator(node, container);
-     *
-     * // Copy constructor / clone
-     * let it1 = new Iterator(node, container);
-     * let it2 = new Iterator(it1);
-     *
-     * // Copy constructor / clone from ReverseIterator instance
-     * let it1 = new ReverseIterator(node, container);
-     * let it2 = new Iterator(it1);
-     */
-    constructor(...args) {
-        if (args.length === 2) {
-            let [node, container] = args;
-            super(node, container);
-        }
-        else if (args.length === 1) {
-            let [obj] = args;
-            let className = obj.constructor.name;
-            if (className === Iterator.name) {
-                super(obj.__n, obj.__c);
-            }
-            // eslint-disable-next-line no-use-before-define
-            else if (className === ReverseIterator.name) {
-                let c = obj.__c;
-                super(c.next(obj.__n), c);
-            }
-            else {
-                throw new Error(`Can't create an Iterator from ${className}`);
-            }
-        }
-        else {
-            throw new Error('Can\'t create an Iterator with provided parameters');
-        }
-    }
-
-    /**
-     * Replaces node reference with the reference of the next node in the container.
-     * Can be used for manual iteration over a range of key-value pairs.
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * let from = t.lowerBound(0);
-     * let to = t.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    next() {
-        /**
-         * __n and __c are defined in the base class
-         */
-        this.__n = this.__c.next(this.__n);
-    }
-
-    /**
-     * Replaces node reference with the reference of the previous node in the container
-     * Can be used for manual reverse iteration over a range of key-value pairs.
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * let from = t.lowerBound(0);
-     * let to = t.upperBound(50);
-     * let it = to;
-     * while (!it.equals(from)) {
-     *   it.prev();
-     *   console.log(it.key);
-     * }
-     */
-    prev() {
-        this.__n = this.__c.prev(this.__n);
-    }
-}
-
-/**
- * STL-like backward iterator. Can be used to traverse container or a range in the reverse order.
- * It's more verbose than ES6 iterators, but allows iteration over any part of the container
- *
- * @example
- * let m = new TreeMap();
- * ...
- * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
- *   console.log(`key: ${it.key}, value: ${it.value}`);
- * }
- */
-class ReverseIterator extends BaseIterator {
-    /**
-     * There are 3 ways to construct a reverse iterator:
-     *
-     * 1. Using a node and a container
-     * 2. Copy constructor / clone
-     * 3. Copy constructor / clone from forward Iterator instance
-     * @param {*} args
-     *
-     * @example
-     * // Using a node and a container
-     * let it = new ReverseIterator(node, container);
-     *
-     * // Copy constructor / clone
-     * let it1 = new ReverseIterator(node, container);
-     * let it2 = new ReverseIterator(it1);
-     *
-     * // Copy constructor / clone from forward Iterator instance
-     * let it1 = new Iterator(node, container);
-     * let it2 = new ReverseIterator(it1);
-     */
-    constructor(...args) {
-        if (args.length === 2) {
-            let [node, container] = args;
-            super(node, container);
-        }
-        else if (args.length === 1) {
-            let [obj] = args;
-            let className = obj.constructor.name;
-            if (className === ReverseIterator.name) {
-                super(obj.__n, obj.__c);
-            }
-            else if (className === Iterator.name) {
-                let c = obj.__c;
-                super(c.prev(obj.__n), c);
-            }
-            else {
-                throw new Error(`Can't create an ReverseIterator from ${className}`);
-            }
-        }
-        else {
-            throw new Error('Can\'t create a Reverse Iterator with provided parameters');
-        }
-    }
-
-    /**
-     *  Replaces node reference with the reference of the previous node in the container, because it works in reverse order
-     * Can be used for manual reverse iteration over a range of key-value pairs.
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * let from = new ReverseIterator(t.upperBound(50));
-     * let to = new ReverseIterator(t.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    next() {
-        /**
-         * __n and __c are defined in the base class
-         */
-        this.__n = this.__c.prev(this.__n);
-    }
-
-    /**
-     *  Replaces node reference with the reference of the next node in the container, because it works in reverse order
-     * Can be used for manual forward iteration over a range of key-value pairs.
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * let from = new ReverseIterator(t.upperBound(50));
-     * let to = new ReverseIterator(t.lowerBound(0));
-     * let it = to;
-     * while (!it.equals(from)) {
-     *   it.prev();
-     *   console.log(it.key);
-     * }
-     */
-    prev() {
-        this.__n = this.__c.next(this.__n);
-    }
-}
-
-module.exports = {
-    Iterator: Iterator,
-    ReverseIterator: ReverseIterator
-};
-
-/***/ }),
-/* 5 */
-/***/ (function(module, exports, __nested_webpack_require_45031__) {
-
-module.exports = __nested_webpack_require_45031__(6);
-
-
-/***/ }),
-/* 6 */
-/***/ (function(module, exports, __nested_webpack_require_45149__) {
-
-/* This is an entry point to the library.
-   It collects all public classes and re-exports them */
-/**@private */
-const {TreeMap} = __nested_webpack_require_45149__(7);
-/**@private */
-const {TreeMultiMap} = __nested_webpack_require_45149__(9);
-/**@private */
-const {TreeSet} = __nested_webpack_require_45149__(10);
-/**@private */
-const {TreeMultiSet} = __nested_webpack_require_45149__(11);
-/**@private */
-const {Iterator, ReverseIterator} = __nested_webpack_require_45149__(4);
-/**@private */
-const {JsIterator, JsReverseIterator} = __nested_webpack_require_45149__(3);
-
-module.exports = {
-    Iterator: Iterator,
-    ReverseIterator: ReverseIterator,
-    JsIterator: JsIterator,
-    JsReverseIterator: JsReverseIterator,
-    TreeMap: TreeMap,
-    TreeMultiMap: TreeMultiMap,
-    TreeSet: TreeSet,
-    TreeMultiSet: TreeMultiSet,
-};
-
-/***/ }),
-/* 7 */
-/***/ (function(module, exports, __nested_webpack_require_46005__) {
-
-/** An implementation of red-black tree */
-const {Tree} = __nested_webpack_require_46005__(2);
-/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
-const {KeyValuePolicy} = __nested_webpack_require_46005__(1);
-/** Node for a red-black tree */
-const {TreeNode} = __nested_webpack_require_46005__(0);
-
-/**
- * TreeMap is an associative container that stores elements formed
- * by a combination of a key value and a mapped value, following a specific order.
- *
- * In a TreeMap, the key values are generally used to sort and uniquely identify
- * the elements, while the mapped values store the content associated to this key.
- * The types of key and mapped value may differ.
- *
- * ## Container properties
- * * **Associative** - Elements in associative containers are referenced by their key
- * and not by their absolute position in the container.
- * * **Ordered** - The elements in the container follow a strict order at all times.
- * All inserted elements are given a position in this order.
- * * **Map** - Each element associates a key to a mapped value. Keys are meant
- * to identify the elements whose main content is the mapped value.
- * * **Unique keys** - No two elements in the container can have equivalent keys.
- *
- * @example
- * let map = new TreeMap();
- * // add few values
- * map.set(1, 'a');
- * map.set(2, 'b');
- * // find a value by key
- * let v = map.get(1); // << 'a'
- * // print all key-value pairs
- * for (let [key, value] of map) {
- *   console.log(`key: ${key}, value: ${value}`);
- * }
- */
-class TreeMap {
-    /*======================================================
-     * Methods of ES6 Map
-     *======================================================*/
-
-    /**
-     * Creates an empty, or a pre-initialized map.
-     * @param {*} [iterable] Another iterable object whose key-value pairs are added into the newly created map.
-     * @example
-     * // Create an empty map
-     * let map1 = new TreeMap();
-     * // Create and initialize map
-     * let map2 = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     */
-    constructor(iterable) {
-        /** Internal tree */
-        this.__t = new Tree();
-        this.__t.valuePolicy = new KeyValuePolicy();
-        if ((iterable !== undefined)
-            && (iterable !== null)) {
-            if (iterable[Symbol.iterator] !== undefined) {
-                // copy contents
-                for (let [k, v] of iterable) {
-                    this.set(k, v);
-                }
-            }
-            else {
-                throw new Error('TreeMap constructor accepts only iterable objects');
-            }
-        }
-    }
-
-    /**
-     * String tag of this class
-     * @returns {String}
-     * @example
-     * Object.prototype.toString.call(new TreeMap()); // "[object TreeMap]"
-     */
-    get [Symbol.toStringTag]() {
-        return 'TreeMap';
-    }
-
-    /**
-     * Allows to create programmatically an instance of the same class
-     * @returns constructor object for this class.
-     * @example
-     * let map = new TreeMap();
-     * let constrFunc = Object.getPrototypeOf(map).constructor[Symbol.species];
-     * let map2 = new constrFunc();
-     */
-    static get [Symbol.species]() {
-        return TreeMap;
-    }
-
-    /**
-     * Removes all key-value pairs.
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.clear();
-     * console.log(map.size); // 0
-     */
-    clear() {
-        this.__t.clear();
-    }
-
-    /**
-     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.delete(2);
-     * console.log(map.toString()); // {1:A,3:C}
-     */
-    delete(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            this.__t.erase(it.node);
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all key-value pairs in ascending order of the keys.
-     * @returns {JsIterator}
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map.entries()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    entries() {
-        return this.__t.entries();
-    }
-
-    /**
-     * Iterates all key-value pairs using a callback in ascending order of the keys.
-     * Note that ES6 specifies the order of key value parameters in the callback differently from for-of loop.
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.forEach(function(value, key, container) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * });
-     */
-    forEach(callback) {
-        for (let [k, v] of this.__t) {
-            callback(v, k, this);
-        }
-    }
-
-    /**
-     * Finds value associated with the specified key. If specified key does not exist then undefined is returned.
-     * @returns {*}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let v = map.get(3); // 'C'
-     * * let v = map.get(4); // returns undefined
-     */
-    get(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return it.value;
-        }
-        else {
-            return undefined;
-        }
-    }
-
-    /**
-     * A boolean indicator whether map contains a key-value pair with the specified key
-     * @returns {Boolean}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let b = map.get(3); // true
-     */
-    has(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order of the keys.
-     * @returns {JsIterator}
-     * @example
-     * // iterate all keys
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let k of map.keys()) {
-     *   console.log(k); // 1, 2, 3
-     * }
-     * // iterate all keys in reverse order
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let k of map.keys().backward()) {
-     *   console.log(k); // 3, 2, 1
-     * }
-     */
-    keys() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Adds or updates key-value pair to the map.
-     * @param {*} key
-     * @param {*} value
-     * @example
-     * let map = new TreeMap();
-     * map.set(1, 'A');
-     */
-    set(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        this.__t.insertOrReplace(n);
-    }
-
-    /**
-     * Number of key-value pairs in the map.
-     * @returns {Number}
-     */
-    get size() {
-        return this.__t.size();
-    }
-
-    /**
-     * Forward ES6 iterator for all values in ascending order of the keys.
-     * @returns {JsITerator}
-     * @example
-     * // iterate all values
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let v of map.values()) {
-     *   console.log(v); // 'A', 'B', 'C'
-     * }
-     * // iterate all values in reverse order
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let v of map.values().backward()) {
-     *   console.log(v); // 'C', 'B', 'A'
-     * }
-     */
-    values() {
-        return this.__t.values();
-    }
-
-    /**
-     * Forward ES6 iterator for all key-value pairs in ascending order of the keys. The same as entries() method
-     * @returns {JsIterator}
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    [Symbol.iterator]() {
-        return this.__t[Symbol.iterator]();
-    }
-
-    /*======================================================
-     * More methods
-     *======================================================*/
-    /**
-     * ES6 reverse iterator for all key-value pairs in descending order of the keys.
-     * @returns {JsReverseIterator}
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map.backwards()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    backward() {
-        return this.__t.backward();
-    }
-
-    /**
-     * Sets custom comparison function if key values are not of primitive types.
-     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
-     *      +1 if the value of rhs is greater than lhs
-     *      -1 if the value of rhs is less than lhs
-     *       0 if values are the same
-     */
-    set compareFunc(func) {
-        this.clear();
-        this.__t.compare = func;
-    }
-
-    /*======================================================
-     * STL-like methods
-     *======================================================*/
-
-    /**
-     * Forward iterator to the first element
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    begin() {
-        return this.__t.begin();
-    }
-
-    /**
-     * Forward iterator to the element following the last element
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    end() {
-        return this.__t.end();
-    }
-
-    /**
-     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * ...
-     * let it = m.find(1);
-     * if (!it.equals(m.end())) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`); // 1, 'A'
-     * }
-     */
-    find(key) {
-        return this.__t.find(key);
-    }
-
-    /**
-     * Adds key-value pair if such key does not exist in the map
-     * @param {*} key
-     * @param {*} value
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let m = new TreeMap();
-     * let res = m.insertUnique(1, 'A');
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints A
-     * }
-     * res = m.insertUnique(1, 'B') // this step has no effect on the map
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // not executed
-     * }
-     */
-    insertUnique(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        return this.__t.insertUnique(n);
-    }
-
-    /**
-     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
-     * @param {*} key
-     * @param {*} value
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let m = new TreeMap();
-     * let res = m.insertOrReplace(1, 'A');
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints A
-     * }
-     * res = m.insertOrReplace(1, 'B') // replaces value on the existing node
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // prints B
-     * }
-     */
-    insertOrReplace(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        return this.__t.insertOrReplace(n);
-    }
-
-    /**
-     * Removes key-value pair for the specified iterator.
-     * @param {Iterator} iterator
-     * @example
-     * let map = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let it = map.find(2);
-     * it.prev();
-     * map.erase(it); // removes a node with key 1
-     * console.log(map.toString()); // {2:B,3:C}
-     */
-    erase(iterator) {
-        this.__t.erase(iterator.node);
-    }
-
-    /**
-     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = m.lowerBound(0);
-     * let to = m.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(m.upperBound(50));
-     * let to = new ReverseIterator(m.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    lowerBound(key) {
-        return this.__t.lowerBound(key);
-    }
-
-    /**
-     * Reverse iterator to the last element.
-     * @returns {ReverseIterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    rbegin() {
-        return this.__t.rbegin();
-    }
-
-    /**
-     * Reverse iterator pointing to before the first element.
-     * @returns {ReverseIterator}
-     * @example
-     * let m = new TreeMap();
-     * ...
-     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    rend() {
-        return this.__t.rend();
-    }
-
-    /**
-     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = m.lowerBound(0);
-     * let to = m.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let m = new TreeMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(m.upperBound(50));
-     * let to = new ReverseIterator(m.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    upperBound(key) {
-        return this.__t.upperBound(key);
-    }
-
-    /**
-     * @returns first key/value pair of the container, or undefined if container is empty
-     * @example
-     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let first = m.first();
-     * if (first) {
-     *   let key = first[0];   // 1
-     *   let value = first[1]; // 'A'
-     * }
-     */
-    first() {
-        return this.__t.first();
-    }
-
-    /**
-     * @returns last key/value pair of the container, or undefined if container is empty
-     * @example
-     * let m = new TreeMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let last = m.last();
-     * if (last) {
-     *   let key = last[0];   // 3
-     *   let value = last[1]; // 'C'
-     * }
-     */
-    last() {
-        return this.__t.last();
-    }
-
-    /**
-     * Serializes contents of the map in the form {key1:value1,key2:value2,...}
-     * @returns {String}
-     */
-    toString() {
-        return this.__t.toString();
-    }
-}
-
-module.exports = {
-    TreeMap: TreeMap,
-};
-
-/***/ }),
-/* 8 */
-/***/ (function(module, exports) {
-
-/**
- * An instance of this class reports whether insert operation was successful.
- * if a node was added, or an existing one replaced then an iterator is provided. Otherwise the value of iterator is undefined
- */
-class InsertionResult {
-    /**
-     * Default constructor
-     * @param {Boolean} wasAdded
-     * @param {Boolean} wasReplaced
-     * @param {Iterator} iterator only provided if the node was added, or replaced
-     */
-    constructor(wasAdded, wasReplaced, iterator) {
-        /**
-         * Boolean flag indicating whether an element was added
-         */
-        this.wasAdded = wasAdded;
-        /**
-         * Boolean flag indicating whether an existing node was updated
-         */
-        this.wasReplaced = wasReplaced;
-        /**
-         * {Iterator} instance pointing to the newly added node
-         */
-        this.iterator = iterator;
-    }
-}
-
-module.exports = {
-    InsertionResult: InsertionResult
-};
-
-/***/ }),
-/* 9 */
-/***/ (function(module, exports, __nested_webpack_require_63461__) {
-
-/** An implementation of red-black tree */
-const {Tree} = __nested_webpack_require_63461__(2);
-/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
-const {KeyValuePolicy} = __nested_webpack_require_63461__(1);
-/** Node for a red-black tree */
-const {TreeNode} = __nested_webpack_require_63461__(0);
-
-/**
- * TreeMultiMap is an associative container that stores elements formed by
- * a combination of a key value and a mapped value, following a specific order,
- * and where multiple elements can have equivalent keys.
- *
- * In a TreeMultiMap, the key values are generally used to sort and uniquely
- * identify the elements, while the mapped values store the content
- * associated to this key. The types of key and mapped value may differ.
- *
- * ## Container properties
- * * **Associative** - Elements in associative containers are referenced
- * by their key and not by their absolute position in the container.
- * * **Ordered** - The elements in the container follow a strict order
- * at all times. All inserted elements are given a position in this order.
- * * **Map** - Each element associates a key to a mapped value. Keys are meant
- * to identify the elements whose main content is the mapped value.
- * * **Multiple equivalent keys** - Multiple elements in the container
- * can have equivalent keys.
- *
- * @example
- * let map = new TreeMultiMap();
- * // add few values
- * map.set(1, 'a');
- * map.set(2, 'b');
- * map.set(2, 'c');
- * // find a value by key
- * let v = map.get(1); // << 'a'
- * find all values for a given key
- * // print all key-value pairs
- * let from = map.lowerBound(2);
- * let to = map.upperBound(2);
- * let it = from;
- * while (!it.equals(to)) {
- *   console.log(it.key);
- *   it.next();
- * }
- */
-class TreeMultiMap {
-    /*======================================================
-     * Methods of ES6 Map
-     *======================================================*/
-
-    /**
-     * Creates an empty, or a pre-initialized map.
-     * @param {*} [iterable] Another iterable object whose key-value pairs are added into the newly created map.
-     * @example
-     * // Create an empty map
-     * let map1 = new TreeMultiMap();
-     * // Create and initialize map
-     * let map2 = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     */
-    constructor(iterable) {
-        /** Internal tree */
-        this.__t = new Tree();
-        this.__t.valuePolicy = new KeyValuePolicy();
-        if ((iterable !== undefined)
-            && (iterable !== null)) {
-            if (iterable[Symbol.iterator] !== undefined) {
-                // copy contents
-                for (let [k, v] of iterable) {
-                    this.set(k, v);
-                }
-            }
-            else {
-                throw new Error('TreeMultiMap constructor accepts only iterable objects');
-            }
-        }
-    }
-
-    /**
-     * String tag of this class
-     * @returns {String}
-     * @example
-     * Object.prototype.toString.call(new TreeMultiMap()); // "[object TreeMultiMap]"
-     */
-    get [Symbol.toStringTag]() {
-        return 'TreeMultiMap';
-    }
-
-    /**
-     * Allows to create programmatically an instance of the same class
-     * @returns constructor object for this class.
-     * @example
-     * let map = new TreeMultiMap();
-     * let constrFunc = Object.getPrototypeOf(map).constructor[Symbol.species];
-     * let map2 = new constrFunc();
-     */
-    static get [Symbol.species]() {
-        return TreeMultiMap;
-    }
-
-    /**
-     * Removes all key-value pairs.
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.clear();
-     * console.log(map.size); // 0
-     */
-    clear() {
-        this.__t.clear();
-    }
-
-    /**
-     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.delete(2);
-     * console.log(map.toString()); // {1:A,3:C}
-     */
-    delete(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            this.__t.erase(it.node);
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all key-value pairs in ascending order of the keys.
-     * @returns {JsIterator}
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map.entries()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    entries() {
-        return this.__t.entries();
-    }
-
-    /**
-     * Iterates all key-value pairs using a callback in ascending order of the keys.
-     * Note that ES6 specifies the order of key value parameters in the callback differently from for-of loop.
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * map.forEach(function(value, key, container) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * });
-     */
-    forEach(callback) {
-        for (let [k, v] of this.__t) {
-            callback(v, k, this);
-        }
-    }
-
-    /**
-     * Finds value associated with the specified key. If specified key does not exist then undefined is returned.
-     * @returns {*}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let v = map.get(3); // 'C'
-     * * let v = map.get(4); // returns undefined
-     */
-    get(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return it.value;
-        }
-        else {
-            return undefined;
-        }
-    }
-
-    /**
-     * A boolean indicator whether map contains a key-value pair with the specified key
-     * @returns {Boolean}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let b = map.get(3); // true
-     */
-    has(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order of the keys.
-     * @returns {JsIterator}
-     * @example
-     * // iterate all keys
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let k of map.keys()) {
-     *   console.log(k); // 1, 2, 3
-     * }
-     * // iterate all keys in reverse order
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let k of map.keys().backward()) {
-     *   console.log(k); // 3, 2, 1
-     * }
-     */
-    keys() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Adds a key-value pair to the map. Multiple key-value pairs with the same key are allowed in TreeMultiMap.
-     * @param {*} key
-     * @param {*} value
-     * @example
-     * let map = new TreeMultiMap();
-     * map.set(1, 'A');
-     * map.set(1, 'B');
-     * map.set(2, 'C');
-     * for (let k of map.values()) {
-     *   console.log(k); // A, B, C
-     * }
-     */
-    set(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        this.__t.insertMulti(n);
-    }
-
-    /**
-     * Number of key-value pairs in the map.
-     * @returns {Number}
-     */
-    get size() {
-        return this.__t.size();
-    }
-
-    /**
-     * Forward ES6 iterator for all values in ascending order of the keys.
-     * @returns {JsITerator}
-     * @example
-     * // iterate all values
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let v of map.values()) {
-     *   console.log(v); // 'A', 'B', 'C'
-     * }
-     * // iterate all values in reverse order
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let v of map.values().backward()) {
-     *   console.log(v); // 'C', 'B', 'A'
-     * }
-     */
-    values() {
-        return this.__t.values();
-    }
-
-    /**
-     * Forward ES6 iterator for all key-value pairs in ascending order of the keys. The same as entries() method
-     * @returns {JsIterator}
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    [Symbol.iterator]() {
-        return this.__t[Symbol.iterator]();
-    }
-
-    /*======================================================
-     * More methods
-     *======================================================*/
-    /**
-     * ES6 reverse iterator for all key-value pairs in descending order of the keys.
-     * @returns {JsReverseIterator}
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * for (let [key,value] of map.backwards()) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    backward() {
-        return this.__t.backward();
-    }
-
-    /**
-     * Sets custom comparison function if key values are not of primitive types.
-     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
-     *      +1 if the value of rhs is greater than lhs
-     *      -1 if the value of rhs is less than lhs
-     *       0 if values are the same
-     */
-    set compareFunc(func) {
-        this.clear();
-        this.__t.compare = func;
-    }
-
-    /*======================================================
-     * STL-like methods
-     *======================================================*/
-
-    /**
-     * Forward iterator to the first element
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ...
-     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    begin() {
-        return this.__t.begin();
-    }
-
-    /**
-     * Forward iterator to the element following the last element
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ...
-     * for (let it = m.begin(); !it.equals(m.end()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    end() {
-        return this.__t.end();
-    }
-
-    /**
-     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * ...
-     * let it = m.find(1);
-     * if (!it.equals(m.end())) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`); // 1, 'A'
-     * }
-     */
-    find(key) {
-        return this.__t.find(key);
-    }
-
-    /**
-     * Adds key-value pair if such key does not exist in the map
-     * @param {*} key
-     * @param {*} value
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let m = new TreeMultiMap();
-     * let res = m.insertUnique(1, 'A');
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints A
-     * }
-     * res = m.insertUnique(1, 'B') // this step has no effect on the map
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // not executed
-     * }
-     */
-    insertUnique(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        return this.__t.insertUnique(n);
-    }
-
-    /**
-     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
-     * @param {*} key
-     * @param {*} value
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let m = new TreeMultiMap();
-     * let res = m.insertOrReplace(1, 'A');
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints A
-     * }
-     * res = m.insertOrReplace(1, 'B') // replaces value on the existing node
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // prints B
-     * }
-     */
-    insertOrReplace(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        return this.__t.insertOrReplace(n);
-    }
-
-    /**
-     * Adds key-value pair. If such key already exists in the map then adds another node with the same key and a new value.
-     * @param {*} key
-     * @param {*} value
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let m = new TreeMultiMap();
-     * let res = m.insertMulti(1, 'A');
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints A
-     * }
-     * res = m.insertMulti(1, 'B') // adds a new node
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.value}`); // prints B
-     *   it.prev();
-     *   console.log(`Previously inserted ${res.iterator.value}`); // prints A
-     * }
-     */
-    insertMulti(key, value) {
-        let n = new TreeNode();
-        n.key = key;
-        n.value = value;
-        return this.__t.insertMulti(n);
-    }
-
-    /**
-     * Removes key-value pair for the specified iterator.
-     * @param {Iterator} iterator
-     * @example
-     * let map = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let it = map.find(2);
-     * it.prev();
-     * map.erase(it); // removes a node with key 1
-     * console.log(map.toString()); // {2:B,3:C}
-     */
-    erase(iterator) {
-        this.__t.erase(iterator.node);
-    }
-
-    /**
-     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = m.lowerBound(0);
-     * let to = m.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let m = new TreeMultiMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(m.upperBound(50));
-     * let to = new ReverseIterator(m.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    lowerBound(key) {
-        return this.__t.lowerBound(key);
-    }
-
-    /**
-     * Reverse iterator to the last element.
-     * @returns {ReverseIterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ...
-     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    rbegin() {
-        return this.__t.rbegin();
-    }
-
-    /**
-     * Reverse iterator pointing to before the first element.
-     * @returns {ReverseIterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ...
-     * for (let it = m.rbegin(); !it.equals(m.rend()); it.next()) {
-     *   console.log(`key: ${it.key}, value: ${it.value}`);
-     * }
-     */
-    rend() {
-        return this.__t.rend();
-    }
-
-    /**
-     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let m = new TreeMultiMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = m.lowerBound(0);
-     * let to = m.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let m = new TreeMultiMap();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(m.upperBound(50));
-     * let to = new ReverseIterator(m.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    upperBound(key) {
-        return this.__t.upperBound(key);
-    }
-
-    /**
-     * @returns first key/value pair of the container, or undefined if container is empty
-     * @example
-     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let first = m.first();
-     * if (first) {
-     *   let key = first[0];   // 1
-     *   let value = first[1]; // 'A'
-     * }
-     */
-    first() {
-        return this.__t.first();
-    }
-
-    /**
-     * @returns last key/value pair of the container, or undefined if container is empty
-     * @example
-     * let m = new TreeMultiMap([[1, 'A'], [2, 'B'], [3, 'C']]);
-     * let last = m.last();
-     * if (last) {
-     *   let key = last[0];   // 3
-     *   let value = last[1]; // 'C'
-     * }
-     */
-    last() {
-        return this.__t.last();
-    }
-
-    /**
-     * Serializes contents of the map in the form {key1:value1,key2:value2,...}
-     * @returns {String}
-     */
-    toString() {
-        return this.__t.toString();
-    }
-}
-
-module.exports = {
-    TreeMultiMap: TreeMultiMap,
-};
-
-/***/ }),
-/* 10 */
-/***/ (function(module, exports, __nested_webpack_require_81431__) {
-
-/** An implementation of red-black tree */
-const {Tree} = __nested_webpack_require_81431__(2);
-/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
-const {KeyOnlyPolicy} = __nested_webpack_require_81431__(1);
-/** Node for a red-black tree */
-const {TreeNode} = __nested_webpack_require_81431__(0);
-
-/**
- * TreeSet is a container that stores unique elements following a specific order.
- *
- * In a TreeSet, the value of an element also identifies it (the value is itself the key),
- * and each value must be unique. The value of the elements in a TreeSet cannot be modified
- * once in the container (the elements are immutable), but they can be inserted or removed
- * from the container.
- *
- * ## Container properties
- * * **Associative** - Elements in associative containers are referenced by their key and
- * not by their absolute position in the container.</li>
- * * **Ordered** - The elements in the container follow a strict order at all times.
- * All inserted elements are given a position in this order.</li>
- * * **Set** - The value of an element is also the key used to identify it.</li>
- * * **Unique keys** - No two elements in the container can have equivalent keys.</li>
- *
- *
- * @example
- * let set = new TreeSet();
- * // add few values
- * set.add(1);
- * set.add(2);
- * // check whether key exists
- * let flag = set.has(1); // << true
- * // print all keys
- * for (let key of set) {
- *   console.log(`key: ${key}`);
- * }
- */
-class TreeSet {
-    /*======================================================
-     * Methods of ES6 Set
-     *======================================================*/
-
-    /**
-     * Creates an empty, or a pre-initialized set.
-     * @param {*} [iterable] Another iterable object whose values are added into the newly created set.
-     * @example
-     * // Create an empty set
-     * let set = new TreeSet();
-     * // Create and initialize set
-     * let set2 = new TreeSet([1, 2, 3]);
-     */
-    constructor(iterable) {
-        /** Internal tree */
-        this.__t = new Tree();
-        this.__t.valuePolicy = new KeyOnlyPolicy();
-        if ((iterable !== undefined)
-            && (iterable !== null)) {
-            if (iterable[Symbol.iterator] !== undefined) {
-                // copy contents
-                for (let k of iterable) {
-                    this.add(k);
-                }
-            }
-            else {
-                throw new Error('TreeSet constructor accepts only iterable objects');
-            }
-        }
-    }
-
-    /**
-     * String tag of this class
-     * @returns {String}
-     * @example
-     * Object.prototype.toString.call(new TreeSet()); // "[object TreeSet]"
-     */
-    get [Symbol.toStringTag]() {
-        return 'TreeSet';
-    }
-
-    /**
-     * Allows to create programmatically an instance of the same class
-     * @returns constructor object for this class.
-     * @example
-     * let set = new TreeSet();
-     * let constrFunc = Object.getPrototypeOf(set).constructor[Symbol.species];
-     * let set2 = new constrFunc();
-     */
-    static get [Symbol.species]() {
-        return TreeSet;
-    }
-
-    /**
-     * Removes all key-value pairs.
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * set.clear();
-     * console.log(set.size); // 0
-     */
-    clear() {
-        this.__t.clear();
-    }
-
-    /**
-     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * set.delete(2);
-     * console.log(set.toString()); // {1,3}
-     */
-    delete(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            this.__t.erase(it.node);
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all values in ascending order.
-     * @returns {JsIterator}
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let key of set.entries()) {
-     *   console.log(`key: ${key}`);
-     * }
-     */
-    entries() {
-        return this.__t.entries();
-    }
-
-    /**
-     * Iterates all values using a callback in ascending order.
-     * Note that ES6 specifies the order of key parameters in the callback differently from for-of loop.
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * set.forEach(function(value, key, container) {
-     *   // value is the same as key
-     *   console.log(`key: ${key}, value: ${value}`);
-     * });
-     */
-    forEach(callback) {
-        for (let k of this.__t) {
-            callback(k, k, this);
-        }
-    }
-
-    /**
-     * A boolean indicator whether set contains the specified key.
-     * @returns {Boolean}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * let b = set.get(3); // true
-     * b = set.get(4); // false
-     */
-    has(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order.
-     * @returns {JsIterator}
-     * @example
-     * // iterate all keys
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let k of set.keys()) {
-     *   console.log(k); // 1, 2, 3
-     * }
-     * // iterate all keys in reverse order
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let k of set.keys().backward()) {
-     *   console.log(k); // 3, 2, 1
-     * }
-     */
-    keys() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Adds a key to the set, unless the key already exists.
-     * @param {*} key
-     * @example
-     * let set = new TreeSet();
-     * set.add(1);
-     */
-    add(key) {
-        let n = new TreeNode();
-        n.key = key;
-        this.__t.insertUnique(n);
-    }
-
-    /**
-     * Number of keys in the set.
-     * @returns {Number}
-     */
-    get size() {
-        return this.__t.size();
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order. It is the same as keys() method
-     * @returns {JsITerator}
-     * @example
-     * // iterate all values
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let v of set.values()) {
-     *   console.log(v); // '1', '2', '3'
-     * }
-     * // iterate all values in reverse order
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let v of set.values().backward()) {
-     *   console.log(v); // '3', '2', '1'
-     * }
-     */
-    values() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order. The same as entries() method
-     * @returns {JsIterator}
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let key of set) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    [Symbol.iterator]() {
-        return this.__t[Symbol.iterator]();
-    }
-
-    /*======================================================
-     * More methods
-     *======================================================*/
-    /**
-     * ES6 reverse iterator for all keys in descending order.
-     * @returns {JsReverseIterator}
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * for (let key of set.backwards()) {
-     *   console.log(`key: ${key}`);
-     * }
-     */
-    backward() {
-        return this.__t.backward();
-    }
-
-    /**
-     * Sets custom comparison function if key values are not of primitive types.
-     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
-     *      +1 if the value of rhs is greater than lhs
-     *      -1 if the value of rhs is less than lhs
-     *       0 if values are the same
-     */
-    set compareFunc(func) {
-        this.clear();
-        this.__t.compare = func;
-    }
-
-    /*======================================================
-     * STL-like methods
-     *======================================================*/
-
-    /**
-     * Forward iterator to the first element
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeSet();
-     * ...
-     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    begin() {
-        return this.__t.begin();
-    }
-
-    /**
-     * Forward iterator to the element following the last element
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeSet();
-     * ...
-     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    end() {
-        return this.__t.end();
-    }
-
-    /**
-     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * ...
-     * let it = set.find(1);
-     * if (!it.equals(set.end())) {
-     *   console.log(`Found key: ${it.key}`); // 1
-     * }
-     */
-    find(key) {
-        return this.__t.find(key);
-    }
-
-    /**
-     * Adds a key if it doesn't exist
-     * @param {*} key
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let set = new TreeSet();
-     * let res = set.insertUnique(1);
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
-     * }
-     * res = set.insertUnique(1); // this step has no effect on the set
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // not executed
-     * }
-     */
-    insertUnique(key) {
-        let n = new TreeNode();
-        n.key = key;
-        return this.__t.insertUnique(n);
-    }
-
-    /**
-     * Adds key-value pair if such key does not exist in the map. Replaces value if such key exists
-     * @param {*} key
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let set = new TreeSet();
-     * let res = set.insertOrReplace(1);
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
-     * }
-     * res = set.insertOrReplace(1) // returns iterator to the previously added node
-     * if (res.wasInserted) {
-     *   console.log(`Inserted ${res.iterator.key}`); // prints 1
-     * }
-     */
-    insertOrReplace(key) {
-        let n = new TreeNode();
-        n.key = key;
-        return this.__t.insertOrReplace(n);
-    }
-
-    /**
-     * Removes value for the specified iterator.
-     * @param {Iterator} iterator
-     * @example
-     * let set = new TreeSet([1,2,3]);
-     * let it = set.find(2);
-     * it.prev();
-     * set.erase(it); // removes a node with key 1
-     * console.log(set.toString()); // {2,3}
-     */
-    erase(iterator) {
-        this.__t.erase(iterator.node);
-    }
-
-    /**
-     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = set.lowerBound(0);
-     * let to = set.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let set = new TreeSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(set.upperBound(50));
-     * let to = new ReverseIterator(set.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    lowerBound(key) {
-        return this.__t.lowerBound(key);
-    }
-
-    /**
-     * Reverse iterator to the last element.
-     * @returns {ReverseIterator}
-     * @example
-     * let set = new TreeSet();
-     * ...
-     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    rbegin() {
-        return this.__t.rbegin();
-    }
-
-    /**
-     * Reverse iterator pointing to before the first element.
-     * @returns {ReverseIterator}
-     * @example
-     * let set = new TreeSet();
-     * ...
-     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    rend() {
-        return this.__t.rend();
-    }
-
-    /**
-     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = set.lowerBound(0);
-     * let to = set.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let set = new TreeSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(set.upperBound(50));
-     * let to = new ReverseIterator(set.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    upperBound(key) {
-        return this.__t.upperBound(key);
-    }
-
-    /**
-     * @returns first element of the container, or undefined if container is empty
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * let first = set.first(); // 1
-     */
-    first() {
-        return this.__t.first();
-    }
-
-    /**
-     * @returns last element of the container, or undefined if container is empty
-     * @example
-     * let set = new TreeSet([1, 2, 3]);
-     * let last = set.last(); // 3
-     */
-    last() {
-        return this.__t.last();
-    }
-
-    /**
-     * Serializes contents of the set in the form {key1,key2,...}
-     * @returns {String}
-     */
-    toString() {
-        return this.__t.toString();
-    }
-}
-
-module.exports = {
-    TreeSet: TreeSet,
-};
-
-/***/ }),
-/* 11 */
-/***/ (function(module, exports, __nested_webpack_require_96249__) {
-
-/** An implementation of red-black tree */
-const {Tree} = __nested_webpack_require_96249__(2);
-/** Classes that regulate whether tree nodes hold keys only, or key-value pairs */
-const {KeyOnlyPolicy} = __nested_webpack_require_96249__(1);
-/** Node for a red-black tree */
-const {TreeNode} = __nested_webpack_require_96249__(0);
-
-/**
- * TreeMultiSet is a container that stores elements following a specific order,
- * and where multiple elements can have equivalent values.
- *
- * In a TreeMultiSet, the value of an element also identifies it
- * (the value is itself the key). The value of the elements in a multiset
- * cannot be modified once in the container (the elements are always immutable),
- * but they can be inserted or removed from the container.
- *
- * ## Container properties
- * * **Associative** - Elements in associative containers are referenced
- * by their key and not by their absolute position in the container.
- * * **Ordered** - The elements in the container follow a strict order
- * at all times. All inserted elements are given a position in this order.
- * * **Set** - The value of an element is also the key used to identify it.
- * * **Multiple equivalent keys** - Multiple elements in the container
- * can have equivalent keys.
- *
- * @example
- * let set = new TreeMultiSet();
- * // add few values
- * set.add(1);
- * set.add(2);
- * set.add(2);
- * // check whether key exists
- * let flag = set.has(1); // << true
- * // print all keys
- * for (let key of set) {
- *   console.log(`key: ${key}`); // 1, 2, 2
- * }
- */
-class TreeMultiSet {
-    /*======================================================
-     * Methods of ES6 Set
-     *======================================================*/
-
-    /**
-     * Creates an empty, or a pre-initialized set.
-     * @param {*} [iterable] Another iterable object whose values are added into the newly created set.
-     * @example
-     * // Create an empty set
-     * let set = new TreeMultiSet();
-     * // Create and initialize set
-     * let set2 = new TreeMultiSet([1, 2, 3]);
-     */
-    constructor(iterable) {
-        /** Internal tree */
-        this.__t = new Tree();
-        this.__t.valuePolicy = new KeyOnlyPolicy();
-        if ((iterable !== undefined)
-            && (iterable !== null)) {
-            if (iterable[Symbol.iterator] !== undefined) {
-                // copy contents
-                for (let k of iterable) {
-                    this.add(k);
-                }
-            }
-            else {
-                throw new Error('TreeMultiSet constructor accepts only iterable objects');
-            }
-        }
-    }
-
-    /**
-     * String tag of this class
-     * @returns {String}
-     * @example
-     * Object.prototype.toString.call(new TreeMultiSet()); // "[object TreeMultiSet]"
-     */
-    get [Symbol.toStringTag]() {
-        return 'TreeMultiSet';
-    }
-
-    /**
-     * Allows to create programmatically an instance of the same class
-     * @returns constructor object for this class.
-     * @example
-     * let set = new TreeMultiSet();
-     * let constrFunc = Object.getPrototypeOf(set).constructor[Symbol.species];
-     * let set2 = new constrFunc();
-     */
-    static get [Symbol.species]() {
-        return TreeMultiSet;
-    }
-
-    /**
-     * Removes all key-value pairs.
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * set.clear();
-     * console.log(set.size); // 0
-     */
-    clear() {
-        this.__t.clear();
-    }
-
-    /**
-     * Removes key-value pair with the specified key if such entry exists. Does nothing otherwise.
-     * @example
-     * let set = new TreeMultiSet([1, 2, 2, 3]);
-     * set.delete(2);
-     * console.log(set.toString()); // {1,2,3}
-     * set.delete(2); / remove the second copy of the key
-     * console.log(set.toString()); // {1,3}
-     */
-    delete(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            this.__t.erase(it.node);
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all values in ascending order.
-     * @returns {JsIterator}
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let key of set.entries()) {
-     *   console.log(`key: ${key}`);
-     * }
-     */
-    entries() {
-        return this.__t.entries();
-    }
-
-    /**
-     * Iterates all values using a callback in ascending order.
-     * Note that ES6 specifies the order of key parameters in the callback differently from for-of loop.
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * set.forEach(function(value, key, container) {
-     *   // value is the same as key
-     *   console.log(`key: ${key}, value: ${value}`);
-     * });
-     */
-    forEach(callback) {
-        for (let k of this.__t) {
-            callback(k, k, this);
-        }
-    }
-
-    /**
-     * A boolean indicator whether set contains the specified key.
-     * @returns {Boolean}
-     * @param {*} key a value of any type that can be compared with a key
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * let b = set.get(3); // true
-     * b = set.get(4); // false
-     */
-    has(key) {
-        let it = this.__t.find(key);
-        if (!it.equals(this.__t.end())) {
-            return true;
-        }
-        else {
-            return false;
-        }
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order.
-     * @returns {JsIterator}
-     * @example
-     * // iterate all keys
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let k of set.keys()) {
-     *   console.log(k); // 1, 2, 3
-     * }
-     * // iterate all keys in reverse order
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let k of set.keys().backward()) {
-     *   console.log(k); // 3, 2, 1
-     * }
-     */
-    keys() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Adds a key to the set. If the key already exists then another entry with the same value is added.
-     * @param {*} key
-     * @example
-     * let set = new TreeMultiSet();
-     * set.add(1);
-     * set.add(1);
-     * set.add(2);
-     * // print all keys
-     * for (let key of set) {
-     *   console.log(`key: ${key}`); // 1, 1, 2
-     * }
-     */
-    add(key) {
-        let n = new TreeNode();
-        n.key = key;
-        this.__t.insertMulti(n);
-    }
-
-    /**
-     * Number of keys in the set.
-     * @returns {Number}
-     */
-    get size() {
-        return this.__t.size();
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order. It is the same as keys() method
-     * @returns {JsITerator}
-     * @example
-     * // iterate all values
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let v of set.values()) {
-     *   console.log(v); // '1', '2', '3'
-     * }
-     * // iterate all values in reverse order
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let v of set.values().backward()) {
-     *   console.log(v); // '3', '2', '1'
-     * }
-     */
-    values() {
-        return this.__t.keys();
-    }
-
-    /**
-     * Forward ES6 iterator for all keys in ascending order. The same as entries() method
-     * @returns {JsIterator}
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let key of set) {
-     *   console.log(`key: ${key}, value: ${value}`);
-     * }
-     */
-    [Symbol.iterator]() {
-        return this.__t[Symbol.iterator]();
-    }
-
-    /*======================================================
-     * More methods
-     *======================================================*/
-    /**
-     * ES6 reverse iterator for all keys in descending order.
-     * @returns {JsReverseIterator}
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * for (let key of set.backwards()) {
-     *   console.log(`key: ${key}`);
-     * }
-     */
-    backward() {
-        return this.__t.backward();
-    }
-
-    /**
-     * Sets custom comparison function if key values are not of primitive types.
-     * Callback is a 3-way comparison function accepts two key values (lhs, rhs). It is expected to return
-     *      +1 if the value of rhs is greater than lhs
-     *      -1 if the value of rhs is less than lhs
-     *       0 if values are the same
-     */
-    set compareFunc(func) {
-        this.clear();
-        this.__t.compare = func;
-    }
-
-    /*======================================================
-     * STL-like methods
-     *======================================================*/
-
-    /**
-     * Forward iterator to the first element
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ...
-     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    begin() {
-        return this.__t.begin();
-    }
-
-    /**
-     * Forward iterator to the element following the last element
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ...
-     * for (let it = set.begin(); !it.equals(set.end()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    end() {
-        return this.__t.end();
-    }
-
-    /**
-     * Finds an element with key equivalent to the specified one. If such key does not exist end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * ...
-     * let it = set.find(1);
-     * if (!it.equals(set.end())) {
-     *   console.log(`Found key: ${it.key}`); // 1
-     * }
-     */
-    find(key) {
-        return this.__t.find(key);
-    }
-
-    /**
-     * Adds key if such key does not exist in the set.
-     * @param {*} key
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let set = new TreeMultiSet();
-     * set.insertUnique(1);
-     * set.insertUnique(1); // this step has no effect on the set
-     * let flag = set.has(1); // true
-     * let size = set.size; // 1
-     */
-    insertUnique(key) {
-        let n = new TreeNode();
-        n.key = key;
-        return this.__t.insertUnique(n);
-    }
-
-    /**
-     * Adds key if such key does not exist in the set. Same as insertUnique()
-     * @param {*} key
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let set = new TreeMultiSet();
-     * set.insertOrReplace(1);
-     * set.insertOrReplace(1); // this step has no effect on the set
-     * let flag = set.has(1); // true
-     * let size = set.size; // 1
-     */
-    insertOrReplace(key) {
-        let n = new TreeNode();
-        n.key = key;
-        return this.__t.insertOrReplace(n);
-    }
-
-    /**
-     * Adds key whether it exists or not in the set.
-     * @param {*} key
-     * @returns {InsertionResult} - indicates whether a node was added and provides iterator to it.
-     * @example
-     * let set = new TreeMultiSet();
-     * set.insertMulti(1);
-     * set.insertMulti(1); // this step has no effect on the map
-     * let flag = set.has(1); // true
-     * let size = set.size; // 2
-     */
-    insertMulti(key) {
-        let n = new TreeNode();
-        n.key = key;
-        return this.__t.insertMulti(n);
-    }
-
-    /**
-     * Removes value for the specified iterator.
-     * @param {Iterator} iterator
-     * @example
-     * let set = new TreeMultiSet([1,2,3]);
-     * let it = set.find(2);
-     * it.prev();
-     * set.erase(it); // removes a node with key 1
-     * console.log(set.toString()); // {2,3}
-     */
-    erase(iterator) {
-        this.__t.erase(iterator.node);
-    }
-
-    /**
-     * Iterator pointing to the first element that is not less than specified key. If no such element is found, see end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = set.lowerBound(0);
-     * let to = set.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let set = new TreeMultiSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(set.upperBound(50));
-     * let to = new ReverseIterator(set.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    lowerBound(key) {
-        return this.__t.lowerBound(key);
-    }
-
-    /**
-     * Reverse iterator to the last element.
-     * @returns {ReverseIterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ...
-     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    rbegin() {
-        return this.__t.rbegin();
-    }
-
-    /**
-     * Reverse iterator pointing to before the first element.
-     * @returns {ReverseIterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ...
-     * for (let it = set.rbegin(); !it.equals(set.rend()); it.next()) {
-     *   console.log(`key: ${it.key}`);
-     * }
-     */
-    rend() {
-        return this.__t.rend();
-    }
-
-    /**
-     * Iterator pointing to the first element that is greater than key. If no such element is found end() iterator is returned.
-     * @param {*} key
-     * @returns {Iterator}
-     * @example
-     * let set = new TreeMultiSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive
-     * let from = set.lowerBound(0);
-     * let to = set.upperBound(50);
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     *
-     * let set = new TreeMultiSet();
-     * ... // add key-value pairs., using numbers as keys
-     * // iterate through all key-value pairs with keys between 0 and 50 inclusive in reverse order
-     * let from = new ReverseIterator(set.upperBound(50));
-     * let to = new ReverseIterator(set.lowerBound(0));
-     * let it = from;
-     * while (!it.equals(to)) {
-     *   console.log(it.key);
-     *   it.next();
-     * }
-     */
-    upperBound(key) {
-        return this.__t.upperBound(key);
-    }
-
-    /**
-     * @returns first element of the container, or undefined if container is empty
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * let first = set.first(); // 1
-     */
-    first() {
-        return this.__t.first();
-    }
-
-    /**
-     * @returns last element of the container, or undefined if container is empty
-     * @example
-     * let set = new TreeMultiSet([1, 2, 3]);
-     * let last = set.last(); // 3
-     */
-    last() {
-        return this.__t.last();
-    }
-
-    /**
-     * Serializes contents of the set in the form {key1,key2,...}
-     * @returns {String}
-     */
-    toString() {
-        return this.__t.toString();
-    }
-}
-
-module.exports = {
-    TreeMultiSet: TreeMultiSet,
-};
-
-/***/ })
-/******/ ]);
-});
-
-/***/ }),
-
 /***/ 742:
 /***/ (function(module, __unusedexports, __webpack_require__) {
 
diff --git a/src/main.ts b/src/main.ts
index 71da8d9..19b00b3 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,9 +1,11 @@
 import * as github from '@actions/github'
 import * as core from '@actions/core'
 import * as rest from '@octokit/rest'
-import * as treemap from 'jstreemap'
 
-const CANCELLABLE_RUNS = [
+/**
+ Those are the cancellable event types tht we know about
+ */
+const CANCELLABLE_EVENT_TYPES = [
   'push',
   'pull_request',
   'workflow_run',
@@ -11,104 +13,190 @@ const CANCELLABLE_RUNS = [
   'workflow_dispatch'
 ]
 
+/**
+ * Those are different modes for cancelling
+ */
 enum CancelMode {
   DUPLICATES = 'duplicates',
+  ALL_DUPLICATES = 'allDuplicates',
   SELF = 'self',
   FAILED_JOBS = 'failedJobs',
   NAMED_JOBS = 'namedJobs'
 }
 
-function createListRunsQueryOtherRuns(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
-  status: string,
-  workflowId: number | string,
-  headBranch: string,
+/**
+ * Stores information uniquely identifying the run by it's triggering source:
+ * It is the workflow id, Head Repo and Branch the change originates from and event name that originates it.
+ *
+ * All Runs coming from the same Pull Request share the same Source Group. Also all pushes to master
+ * share the same Source Group
+ */
+interface WorkflowRunSourceGroup {
+  workflowId: number | string
+  headRepo: string
+  headBranch: string
+  eventName: string
+}
+
+/**
+ * Stores information about the owner and repository used, as well as octokit object that is used for
+ * authentication.
+ */
+interface RepositoryInfo {
+  octokit: github.GitHub
+  owner: string
+  repo: string
+}
+
+/**
+ * Stores information about the workflow info that triggered the current workflow.
+ */
+interface TriggeringWorkflowInfo {
+  headRepo: string
+  headBranch: string
+  headSha: string
   eventName: string
+  mergeCommitSha: string | null
+  pullRequest: rest.PullsListResponseItem | null
+}
+
+/**
+ * Converts the source of a run object into a string that can be used as map key in maps where we keep
+ * arrays of runs per source group
+ * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @returns the unique string id for the source group
+ */
+function getSourceGroupId(sourceGroup: WorkflowRunSourceGroup): string {
+  return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`
+}
+
+/**
+ * Creates query parameters selecting all runs that share the same source group as we have. This can
+ * be used to select duplicates of my own run.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param mySourceGroup - source group of the originating run
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
+function createListRunsQueryRunsSameSource(
+  repositoryInfo: RepositoryInfo,
+  status: string,
+  mySourceGroup: WorkflowRunSourceGroup
 ): rest.RequestOptions {
   const request = {
-    owner,
-    repo,
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    workflow_id: workflowId,
+    workflow_id: mySourceGroup.workflowId,
     status,
-    branch: headBranch,
-    event: eventName
+    branch: mySourceGroup.headBranch,
+    event: mySourceGroup.eventName
   }
-  return octokit.actions.listWorkflowRuns.endpoint.merge(request)
+  return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
-
-function createListRunsQueryMyOwnRun(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+/**
+ * Creates query parameters selecting only specific run Id.
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param workflowId - Id of the workflow to retrieve
+ * @param runId - run Id to retrieve
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
+function createListRunsQuerySpecificRunId(
+  repositoryInfo: RepositoryInfo,
   status: string,
   workflowId: number | string,
   runId: number
 ): rest.RequestOptions {
   const request = {
-    owner,
-    repo,
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     workflow_id: workflowId,
     status,
     // eslint-disable-next-line @typescript-eslint/camelcase
     run_id: runId.toString()
   }
-  return octokit.actions.listWorkflowRuns.endpoint.merge(request)
+  return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
 
+/**
+ * Creates query parameters selecting all run Ids for specified workflow Id.
+ * @param repositoryInfo - information about the repository used
+ * @param status - status of the run that we are querying for
+ * @param workflowId - Id of the workflow to retrieve
+ * @return query parameters merged with the listWorkflowRuns criteria
+ */
 function createListRunsQueryAllRuns(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+  repositoryInfo: RepositoryInfo,
   status: string,
   workflowId: number | string
 ): rest.RequestOptions {
   const request = {
-    owner,
-    repo,
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     workflow_id: workflowId,
     status
   }
-  return octokit.actions.listWorkflowRuns.endpoint.merge(request)
+  return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
 
+/**
+ * Creates query parameters selecting all jobs for specified run Id.
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @return query parameters merged with the listJobsForWorkflowRun criteria
+ */
 function createJobsForWorkflowRunQuery(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+  repositoryInfo: RepositoryInfo,
   runId: number
 ): rest.RequestOptions {
   const request = {
-    owner,
-    repo,
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     run_id: runId
   }
-  return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request)
+  return repositoryInfo.octokit.actions.listJobsForWorkflowRun.endpoint.merge(
+    request
+  )
 }
 
-function matchInArray(s: string, regexps: string[]): boolean {
+/**
+ * Returns true if the string matches any of the regexps in array of regexps
+ * @param stringToMatch string to match
+ * @param regexps array of regexp to match the string against
+ * @return true if there is a match
+ */
+function matchInArray(stringToMatch: string, regexps: string[]): boolean {
   for (const regexp of regexps) {
-    if (s.match(regexp)) {
+    if (stringToMatch.match(regexp)) {
       return true
     }
   }
   return false
 }
 
+/**
+ * Returns true if the runId specified has jobs matching the regexp and optionally checks if those
+ * jobs are failed.
+ * * If checkIfFailed is False, it returns true if any of the job name for the run match any of the regexps
+ * * Id checkIfFailed is True, it returns true if any of the matching jobs have status 'failed'
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @param jobNameRegexps - array of job name regexps
+ * @param checkIfFailed - whether to check the 'failed' status of matched jobs
+ * @return true if there is a match
+ */
 async function jobsMatchingNames(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+  repositoryInfo: RepositoryInfo,
   runId: number,
   jobNameRegexps: string[],
   checkIfFailed: boolean
 ): Promise<boolean> {
-  const listJobs = createJobsForWorkflowRunQuery(octokit, owner, repo, runId)
+  const listJobs = createJobsForWorkflowRunQuery(repositoryInfo, runId)
   if (checkIfFailed) {
     core.info(
       `\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps} that failed\n`
@@ -118,7 +206,7 @@ async function jobsMatchingNames(
       `\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`
     )
   }
-  for await (const item of octokit.paginate.iterator(listJobs)) {
+  for await (const item of repositoryInfo.octokit.paginate.iterator(listJobs)) {
     for (const job of item.data.jobs) {
       core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`)
       if (matchInArray(job.name, jobNameRegexps)) {
@@ -126,12 +214,14 @@ async function jobsMatchingNames(
           // Only fail the build if one of the matching jobs fail
           if (job.conclusion === 'failure') {
             core.info(
-              `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed. Cancelling run.`
+              `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
+                ` Cancelling run.`
             )
             return true
           } else {
             core.info(
-              `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. So far, so good.`
+              `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
+                ` So far, so good.`
             )
           }
         } else {
@@ -147,41 +237,62 @@ async function jobsMatchingNames(
   return false
 }
 
+/**
+ * Retrieves workflowId from the workflow URL.
+ * @param workflowUrl workflow URL to retrieve the ID from
+ * @return numerical workflow id
+ */
+function retrieveWorkflowIdFromUrl(workflowUrl: string): number {
+  const workflowIdString = workflowUrl.split('/').pop() || ''
+  if (!(workflowIdString.length > 0)) {
+    throw new Error('Could not resolve workflow')
+  }
+  return parseInt(workflowIdString)
+}
+
+/**
+ * Returns workflowId of the runId specified
+ * @param repositoryInfo - information about the repository used
+ * @param runId - Id of the run to retrieve jobs for
+ * @return workflow ID for the run id
+ */
 async function getWorkflowId(
-  octokit: github.GitHub,
-  runId: number,
-  owner: string,
-  repo: string
+  repositoryInfo: RepositoryInfo,
+  runId: number
 ): Promise<number> {
-  const reply = await octokit.actions.getWorkflowRun({
-    owner,
-    repo,
+  const reply = await repositoryInfo.octokit.actions.getWorkflowRun({
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     run_id: runId
   })
   core.info(`The source run ${runId} is in ${reply.data.workflow_url} workflow`)
-  const workflowIdString = reply.data.workflow_url.split('/').pop() || ''
-  if (!(workflowIdString.length > 0)) {
-    throw new Error('Could not resolve workflow')
-  }
-  return parseInt(workflowIdString)
+  return retrieveWorkflowIdFromUrl(reply.data.workflow_url)
 }
 
+/**
+ * Returns workflow runs matching the callable adding query criteria
+ * @param repositoryInfo - information about the repository used
+ * @param statusValues - array of string status values for runs that we are interested at
+ * @param cancelMode - which cancel mode the query is about
+ * @param createListRunQuery - what is the callable criteria selection
+ * @return map of workflow run items indexed by the workflow run number
+ */
 async function getWorkflowRuns(
-  octokit: github.GitHub,
+  repositoryInfo: RepositoryInfo,
   statusValues: string[],
   cancelMode: CancelMode,
   createListRunQuery: CallableFunction
-): Promise<
-  treemap.TreeMap<number, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem>
-> {
-  const workflowRuns = new treemap.TreeMap<
+): Promise<Map<number, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem>> {
+  const workflowRuns = new Map<
     number,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
   >()
   for (const status of statusValues) {
     const listRuns = await createListRunQuery(status)
-    for await (const item of octokit.paginate.iterator(listRuns)) {
+    for await (const item of repositoryInfo.octokit.paginate.iterator(
+      listRuns
+    )) {
       // There is some sort of bug where the pagination URLs point to a
       // different endpoint URL which trips up the resulting representation
       // In that case, fallback to the actual REST 'workflow_runs' property
@@ -196,10 +307,134 @@ async function getWorkflowRuns(
   return workflowRuns
 }
 
-async function shouldBeCancelled(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+/**
+ * True if the request is candidate for cancelling in case of duplicate deletion
+ * @param runItem item to check
+ * @param headRepo head Repo that we are checking against
+ * @param cancelFutureDuplicates whether future duplicates are being cancelled
+ * @param sourceRunId the source Run Id that originates the request
+ * @return true if we determine that the run Id should be cancelled
+ */
+function isCandidateForCancellingDuplicate(
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  headRepo: string,
+  cancelFutureDuplicates: boolean,
+  sourceRunId: number
+): boolean {
+  const runHeadRepo = runItem.head_repository.full_name
+  if (headRepo !== undefined && runHeadRepo !== headRepo) {
+    core.info(
+      `\nThe run ${runItem.id} is from a different ` +
+        `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`
+    )
+    return false
+  }
+  if (cancelFutureDuplicates) {
+    core.info(
+      `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
+    )
+    return true
+  } else {
+    if (runItem.id === sourceRunId) {
+      core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`)
+      return false
+    } else if (runItem.id > sourceRunId) {
+      core.info(
+        `\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`
+      )
+      return false
+    }
+    core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`)
+    return true
+  }
+}
+
+/**
+ * Should the run is candidate for cancelling in SELF cancelling mode?
+ * @param runItem run item
+ * @param sourceRunId source run id
+ * @return true if the run item is self
+ */
+function isCandidateForCancellingSelf(
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  sourceRunId: number
+): boolean {
+  if (runItem.id === sourceRunId) {
+    core.info(`\nReturning the "source" run: ${runItem.id}.\n`)
+    return true
+  } else {
+    return false
+  }
+}
+
+/**
+ * Should the run is candidate for cancelling in naming job cancelling mode?
+ * @param repositoryInfo - information about the repository used
+ * @param runItem run item
+ * @param jobNamesRegexps array of regexps to match job names against
+ * @return true if the run item contains jobs with names matching the pattern
+ */
+async function isCandidateForCancellingNamedJobs(
+  repositoryInfo: RepositoryInfo,
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  jobNamesRegexps: string[]
+): Promise<boolean> {
+  // Cancel all jobs that have failed jobs (no matter when started)
+  if (
+    await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)
+  ) {
+    core.info(
+      `\nSome jobs have matching names in ${runItem.id} . Returning it.\n`
+    )
+    return true
+  } else {
+    core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`)
+    return false
+  }
+}
+
+/**
+ * Should the run is candidate for cancelling in failed job cancelling mode?
+ * @param repositoryInfo - information about the repository used
+ * @param runItem run item
+ * @param jobNamesRegexps array of regexps to match job names against
+ * @return true if the run item contains failed jobs with names matching the pattern
+ */
+async function isCandidateForCancellingFailedJobs(
+  repositoryInfo: RepositoryInfo,
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  jobNamesRegexps: string[]
+): Promise<boolean> {
+  // Cancel all jobs that have failed jobs (no matter when started)
+  if (
+    await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)
+  ) {
+    core.info(
+      `\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`
+    )
+    return true
+  } else {
+    core.info(
+      `\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`
+    )
+    return false
+  }
+}
+
+/**
+ * Determines whether the run is candidate to be cancelled depending on the mode used
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param headRepo - head repository
+ * @param cancelMode - cancel mode
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param sourceRunId - what is the source run id
+ * @param jobNamesRegexps - what are the regexps for job names
+ * @param skipEventTypes - which events should be skipped
+ * @return true if the run id is candidate for cancelling
+ */
+async function isCandidateForCancelling(
+  repositoryInfo: RepositoryInfo,
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
   headRepo: string,
   cancelMode: CancelMode,
@@ -212,9 +447,10 @@ async function shouldBeCancelled(
     core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`)
     return false
   }
-  if (!CANCELLABLE_RUNS.includes(runItem.event.toString())) {
+  if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
     core.info(
-      `\nThe run ${runItem.id} is (${runItem.event} event - not in ${CANCELLABLE_RUNS}). Not cancelling it.\n`
+      `\nThe run ${runItem.id} is (${runItem.event} event - not ` +
+        `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`
     )
     return false
   }
@@ -226,83 +462,31 @@ async function shouldBeCancelled(
     return false
   }
   if (cancelMode === CancelMode.FAILED_JOBS) {
-    // Cancel all jobs that have failed jobs (no matter when started)
-    if (
-      await jobsMatchingNames(
-        octokit,
-        owner,
-        repo,
-        runItem.id,
-        jobNamesRegexps,
-        true
-      )
-    ) {
-      core.info(
-        `\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`
-      )
-      return true
-    } else {
-      core.info(
-        `\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`
-      )
-      return false
-    }
+    return await isCandidateForCancellingFailedJobs(
+      repositoryInfo,
+      runItem,
+      jobNamesRegexps
+    )
   } else if (cancelMode === CancelMode.NAMED_JOBS) {
-    // Cancel all jobs that have failed jobs (no matter when started)
-    if (
-      await jobsMatchingNames(
-        octokit,
-        owner,
-        repo,
-        runItem.id,
-        jobNamesRegexps,
-        false
-      )
-    ) {
-      core.info(
-        `\nSome jobs have matching names in ${runItem.id} . Returning it.\n`
-      )
-      return true
-    } else {
-      core.info(
-        `\nNone of the jobs match name in ${runItem.id}. Returning it.\n`
-      )
-      return false
-    }
+    return await isCandidateForCancellingNamedJobs(
+      repositoryInfo,
+      runItem,
+      jobNamesRegexps
+    )
   } else if (cancelMode === CancelMode.SELF) {
-    if (runItem.id === sourceRunId) {
-      core.info(`\nReturning the "source" run: ${runItem.id}.\n`)
-      return true
-    } else {
-      return false
-    }
+    return isCandidateForCancellingSelf(runItem, sourceRunId)
   } else if (cancelMode === CancelMode.DUPLICATES) {
-    const runHeadRepo = runItem.head_repository.full_name
-    if (headRepo !== undefined && runHeadRepo !== headRepo) {
-      core.info(
-        `\nThe run ${runItem.id} is from a different ` +
-          `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`
-      )
-      return false
-    }
-    if (cancelFutureDuplicates) {
-      core.info(
-        `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
-      )
-      return true
-    } else {
-      if (runItem.id === sourceRunId) {
-        core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`)
-        return false
-      } else if (runItem.id > sourceRunId) {
-        core.info(
-          `\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`
-        )
-        return false
-      }
-      core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`)
-      return true
-    }
+    return isCandidateForCancellingDuplicate(
+      runItem,
+      headRepo,
+      cancelFutureDuplicates,
+      sourceRunId
+    )
+  } else if (cancelMode === CancelMode.ALL_DUPLICATES) {
+    core.info(
+      `Returning candidate ${runItem.id} for "all_duplicates" cancelling.`
+    )
+    return true
   } else {
     throw Error(
       `\nWrong cancel mode ${cancelMode}! This should never happen.\n`
@@ -310,17 +494,20 @@ async function shouldBeCancelled(
   }
 }
 
+/**
+ * Cancels the specified workflow run.
+ * @param repositoryInfo - information about the repository used
+ * @param runId - run Id to cancel
+ */
 async function cancelRun(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+  repositoryInfo: RepositoryInfo,
   runId: number
 ): Promise<void> {
   let reply
   try {
-    reply = await octokit.actions.cancelWorkflowRun({
-      owner,
-      repo,
+    reply = await repositoryInfo.octokit.actions.cancelWorkflowRun({
+      owner: repositoryInfo.owner,
+      repo: repositoryInfo.repo,
       // eslint-disable-next-line @typescript-eslint/camelcase
       run_id: runId
     })
@@ -332,72 +519,71 @@ async function cancelRun(
   }
 }
 
-async function findAndCancelRuns(
-  octokit: github.GitHub,
-  selfRunId: number,
+/**
+ * Returns map of workflow run items matching the criteria specified group by workflow run id
+ * @param repositoryInfo - information about the repository used
+ * @param statusValues - status values we want to check
+ * @param cancelMode - cancel mode to use
+ * @param sourceWorkflowId - source workflow id
+ * @param sourceRunId - source run id
+ * @param sourceEventName - name of the source event
+ * @param mySourceGroup - source of the run that originated it
+ * @return map of the run items matching grouped by workflow run id
+ */
+async function getWorkflowRunsMatchingCriteria(
+  repositoryInfo: RepositoryInfo,
+  statusValues: string[],
+  cancelMode:
+    | CancelMode
+    | CancelMode.DUPLICATES
+    | CancelMode.ALL_DUPLICATES
+    | CancelMode.FAILED_JOBS
+    | CancelMode.NAMED_JOBS,
   sourceWorkflowId: number | string,
   sourceRunId: number,
-  owner: string,
-  repo: string,
-  headRepo: string,
-  headBranch: string,
   sourceEventName: string,
-  cancelMode: CancelMode,
-  cancelFutureDuplicates: boolean,
-  notifyPRCancel: boolean,
-  notifyPRMessageStart: string,
-  jobNameRegexps: string[],
-  skipEventTypes: string[],
-  reason: string
-): Promise<number[]> {
-  const statusValues = ['queued', 'in_progress']
-  const workflowRuns = await getWorkflowRuns(
-    octokit,
+  mySourceGroup: WorkflowRunSourceGroup
+): Promise<Map<number, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem>> {
+  return await getWorkflowRuns(
+    repositoryInfo,
     statusValues,
     cancelMode,
     function(status: string) {
       if (cancelMode === CancelMode.SELF) {
         core.info(
-          `\nFinding runs for my own run: Owner: ${owner}, Repo: ${repo}, ` +
+          `\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
             `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`
         )
-        return createListRunsQueryMyOwnRun(
-          octokit,
-          owner,
-          repo,
+        return createListRunsQuerySpecificRunId(
+          repositoryInfo,
           status,
           sourceWorkflowId,
           sourceRunId
         )
       } else if (
         cancelMode === CancelMode.FAILED_JOBS ||
-        cancelMode === CancelMode.NAMED_JOBS
+        cancelMode === CancelMode.NAMED_JOBS ||
+        cancelMode === CancelMode.ALL_DUPLICATES
       ) {
         core.info(
-          `\nFinding runs for all runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
-            `Workflow ID:${sourceWorkflowId}\n`
+          `\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
+            `Status: ${status} Workflow ID:${sourceWorkflowId}\n`
         )
         return createListRunsQueryAllRuns(
-          octokit,
-          owner,
-          repo,
+          repositoryInfo,
           status,
           sourceWorkflowId
         )
       } else if (cancelMode === CancelMode.DUPLICATES) {
         core.info(
-          `\nFinding duplicate runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
-            `Workflow ID:${sourceWorkflowId}, Head Branch: ${headBranch},` +
+          `\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
+            `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
             `Event name: ${sourceEventName}\n`
         )
-        return createListRunsQueryOtherRuns(
-          octokit,
-          owner,
-          repo,
+        return createListRunsQueryRunsSameSource(
+          repositoryInfo,
           status,
-          sourceWorkflowId,
-          headBranch,
-          sourceEventName
+          mySourceGroup
         )
       } else {
         throw Error(
@@ -406,18 +592,105 @@ async function findAndCancelRuns(
       }
     }
   )
-  const workflowsToCancel: [number, string][] = []
-  const pullRequestToNotify: number[] = []
+}
+
+/**
+ * Finds pull request matching its headRepo, headBranch and headSha
+ * @param repositoryInfo - information about the repository used
+ * @param headRepo - head repository from which Pull Request comes
+ * @param headBranch - head branch from which Pull Request comes
+ * @param headSha - sha for the head of the incoming Pull request
+ */
+async function findPullRequest(
+  repositoryInfo: RepositoryInfo,
+  headRepo: string,
+  headBranch: string,
+  headSha: string
+): Promise<rest.PullsListResponseItem | null> {
+  // Finds Pull request for this workflow run
+  core.info(
+    `\nFinding PR request id for: owner: ${repositoryInfo.owner}, Repo:${repositoryInfo.repo},` +
+      ` Head:${headRepo}:${headBranch}.\n`
+  )
+  const pullRequests = await repositoryInfo.octokit.pulls.list({
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
+    head: `${headRepo}:${headBranch}`
+  })
+  for (const pullRequest of pullRequests.data) {
+    core.info(
+      `\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`
+    )
+    if (pullRequest.head.sha === headSha) {
+      core.info(`\nFound PR: ${pullRequest.number}\n`)
+      return pullRequest
+    }
+  }
+  core.info(`\nCould not find the PR for this build :(\n`)
+  return null
+}
+
+/**
+ * Finds pull request id for the run item.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run Item that the pull request should be found for
+ * @return pull request number to notify (or undefined if not found)
+ */
+async function findPullRequestForRunItem(
+  repositoryInfo: RepositoryInfo,
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
+): Promise<number | undefined> {
+  const pullRequest = await findPullRequest(
+    repositoryInfo,
+    runItem.head_repository.owner.login,
+    runItem.head_branch,
+    runItem.head_sha
+  )
+  if (pullRequest) {
+    return pullRequest.number
+  }
+  return undefined
+}
+
+/**
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
+ * same group are put together in one array - in a map indexed by the source group id.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param headRepo - head repository the event comes from
+ * @param cancelMode - cancel mode to use
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param sourceRunId - source run id for the run
+ * @param jobNameRegexps - regexps for job names
+ * @param skipEventTypes - array of event names to skip
+ * @param workflowRuns - map of workflow runs found
+ * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ */
+async function filterAndMapWorkflowRunsToGroups(
+  repositoryInfo: RepositoryInfo,
+  headRepo: string,
+  cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
+  sourceRunId: number,
+  jobNameRegexps: string[],
+  skipEventTypes: string[],
+  workflowRuns: Map<
+    number,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
+  >
+): Promise<
+  Map<string, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]>
+> {
+  const mapOfWorkflowRunCandidates = new Map()
   for (const [key, runItem] of workflowRuns) {
     core.info(
       `\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
         ` Created at ${runItem.created_at}\n`
     )
     if (
-      await shouldBeCancelled(
-        octokit,
-        owner,
-        repo,
+      await isCandidateForCancelling(
+        repositoryInfo,
         runItem,
         headRepo,
         cancelMode,
@@ -427,135 +700,287 @@ async function findAndCancelRuns(
         skipEventTypes
       )
     ) {
-      if (notifyPRCancel && runItem.event === 'pull_request') {
-        const pullRequest = await findPullRequest(
-          octokit,
-          owner,
-          repo,
-          runItem.head_repository.owner.login,
-          runItem.head_branch,
-          runItem.head_sha
-        )
-        if (pullRequest) {
-          pullRequestToNotify.push(pullRequest.number)
-        }
+      const candidateSourceGroup: WorkflowRunSourceGroup = {
+        workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
+        headBranch: runItem.head_branch,
+        headRepo: runItem.head_repository.full_name,
+        eventName: runItem.event
+      }
+      const sourceGroupId = getSourceGroupId(candidateSourceGroup)
+      let workflowRunArray:
+        | rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+        | undefined = mapOfWorkflowRunCandidates.get(sourceGroupId)
+      if (workflowRunArray === undefined) {
+        workflowRunArray = []
+        mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray)
       }
-      workflowsToCancel.push([runItem.id, runItem.created_at])
-    }
-  }
-  // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
-  const sortedRunTuplesToCancel = workflowsToCancel.sort(
-    (runTuple1, runTuple2) => runTuple2[1].localeCompare(runTuple1[1])
-  )
-  if (sortedRunTuplesToCancel.length > 0) {
-    if (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) {
       core.info(
-        `\nSkipping the first run (${sortedRunTuplesToCancel[0]}) of all the matching ` +
-          `duplicates - this one we are going to leave in peace!\n`
+        `The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`
       )
-      sortedRunTuplesToCancel.shift()
-    }
-    if (sortedRunTuplesToCancel.length === 0) {
-      core.info(`\nNo duplicates to cancel!\n`)
-      return sortedRunTuplesToCancel.map(runTuple => runTuple[0])
+      workflowRunArray.push(runItem)
     }
-    core.info(
-      '\n######  Cancelling runs starting from the oldest  ##########\n' +
-        `\n     Runs to cancel: ${sortedRunTuplesToCancel.length}\n` +
-        `\n     PRs to notify: ${pullRequestToNotify.length}\n`
-    )
-    for (const runTuple of sortedRunTuplesToCancel) {
-      core.info(`\nCancelling run: ${runTuple}.\n`)
-      await cancelRun(octokit, owner, repo, runTuple[0])
-    }
-    for (const pullRequestNumber of pullRequestToNotify) {
-      const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`
-      await addCommentToPullRequest(
-        octokit,
-        owner,
-        repo,
-        pullRequestNumber,
-        `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`
-      )
-    }
-    core.info(
-      '\n######  Finished cancelling runs                  ##########\n'
-    )
-  } else {
-    core.info(
-      '\n######  There are no runs to cancel!              ##########\n'
-    )
   }
-  return sortedRunTuplesToCancel.map(runTuple => runTuple[0])
-}
-
-function getRequiredEnv(key: string): string {
-  const value = process.env[key]
-  if (value === undefined) {
-    const message = `${key} was not defined.`
-    throw new Error(message)
-  }
-  return value
+  return mapOfWorkflowRunCandidates
 }
 
+/**
+ * Add specified comment to Pull Request
+ * @param repositoryInfo - information about the repository used
+ * @param pullRequestNumber - number of pull request
+ * @param comment - comment to add
+ */
 async function addCommentToPullRequest(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
+  repositoryInfo: RepositoryInfo,
   pullRequestNumber: number,
   comment: string
 ): Promise<void> {
   core.info(`\nNotifying PR: ${pullRequestNumber} with '${comment}'.\n`)
-  await octokit.issues.createComment({
-    owner,
-    repo,
+  await repositoryInfo.octokit.issues.createComment({
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     issue_number: pullRequestNumber,
     body: comment
   })
 }
 
-async function findPullRequest(
-  octokit: github.GitHub,
-  owner: string,
-  repo: string,
-  headRepo: string,
-  headBranch: string,
-  headSha: string
-): Promise<rest.PullsListResponseItem | null> {
-  // Finds Pull request for this workflow run
+/**
+ * Notifies PR about cancelling
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - my own run id
+ * @param pullRequestNumber - number of pull request
+ * @param reason reason for canceling
+ */
+async function notifyPR(
+  repositoryInfo: RepositoryInfo,
+  selfRunId: number,
+  pullRequestNumber: number,
+  reason: string
+): Promise<void> {
+  const selfWorkflowRunUrl =
+    `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
+    `/actions/runs/${selfRunId}`
+  await addCommentToPullRequest(
+    repositoryInfo,
+    pullRequestNumber,
+    `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`
+  )
+}
+
+/**
+ * Cancels all runs in the specified group of runs.
+ * @param repositoryInfo - information about the repository used
+ * @param sortedRunItems - items sorted in descending order (descending order by created_at)
+ * @param notifyPRCancel - whether to notify the PR when cancelling
+ * @param selfRunId - what is the self run id
+ * @param sourceGroupId - what is the source group id
+ * @param reason - reason for canceling
+ */
+async function cancelAllRunsInTheSourceGroup(
+  repositoryInfo: RepositoryInfo,
+  sortedRunItems: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[],
+  notifyPRCancel: boolean,
+  selfRunId: number,
+  sourceGroupId: string,
+  reason: string
+): Promise<number[]> {
   core.info(
-    `\nFinding PR request id for: owner: ${owner}, Repo:${repo}, Head:${headRepo}:${headBranch}.\n`
+    `\n###### Cancelling runs for ${sourceGroupId} starting from the most recent  ##########\n` +
+      `\n     Number of runs to cancel: ${sortedRunItems.length}\n`
   )
-  const pullRequests = await octokit.pulls.list({
-    owner,
-    repo,
-    head: `${headRepo}:${headBranch}`
-  })
-  for (const pullRequest of pullRequests.data) {
-    core.info(
-      `\nComparing: ${pullRequest.number} sha: ${pullRequest.head.sha} with expected: ${headSha}.\n`
+  const cancelledRuns: number[] = []
+  for (const runItem of sortedRunItems) {
+    if (notifyPRCancel && runItem.event === 'pull_request') {
+      const pullRequestNumber = await findPullRequestForRunItem(
+        repositoryInfo,
+        runItem
+      )
+      if (pullRequestNumber !== undefined) {
+        core.info(
+          `\nNotifying PR: ${pullRequestNumber} (runItem: ${runItem}) with: ${reason}\n`
+        )
+        await notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason)
+      }
+    }
+    core.info(`\nCancelling run: ${runItem}.\n`)
+    await cancelRun(repositoryInfo, runItem.id)
+    cancelledRuns.push(runItem.id)
+  }
+  core.info(
+    `\n######  Finished cancelling runs for ${sourceGroupId} ##########\n`
+  )
+  return cancelledRuns
+}
+
+/**
+ * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
+ * descending according to create date in each of the source groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicates.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param cancelMode - cancel mode
+ * @param cancelFutureDuplicates - whether to cancel future duplicates
+ * @param notifyPRCancel - whether to notify PRs with comments
+ * @param selfRunId - self run Id
+ * @param reason - reason for canceling
+ */
+async function cancelTheRunsPerGroup(
+  repositoryInfo: RepositoryInfo,
+  mapOfWorkflowRunsCandidatesToCancel: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >,
+  cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
+  notifyPRCancel: boolean,
+  selfRunId: number,
+  reason: string
+): Promise<number[]> {
+  const cancelledRuns: number[] = []
+  for (const [
+    sourceGroupId,
+    candidatesArray
+  ] of mapOfWorkflowRunsCandidatesToCancel) {
+    // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
+    const sortedRunItems = candidatesArray.sort((runItem1, runItem2) =>
+      runItem2.created_at.localeCompare(runItem1.created_at)
     )
-    if (pullRequest.head.sha === headSha) {
-      core.info(`\nFound PR: ${pullRequest.number}\n`)
-      return pullRequest
+    if (sortedRunItems.length > 0) {
+      if (
+        (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
+        cancelMode === CancelMode.ALL_DUPLICATES
+      ) {
+        core.info(
+          `\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
+            `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`
+        )
+        sortedRunItems.shift()
+      }
+      if (sortedRunItems.length === 0) {
+        core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`)
+        continue
+      }
+      cancelledRuns.push(
+        ...(await cancelAllRunsInTheSourceGroup(
+          repositoryInfo,
+          sortedRunItems,
+          notifyPRCancel,
+          selfRunId,
+          sourceGroupId,
+          reason
+        ))
+      )
+    } else {
+      core.info(
+        `\n######  There are no runs to cancel for ${sourceGroupId} ##########\n`
+      )
     }
   }
-  core.info(`\nCould not find the PR for this build :(\n`)
-  return null
+  return cancelledRuns
+}
+
+/**
+ * Find and cancels runs based on the criteria chosen.
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - source workflow id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param sourceRunId - source run id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param headRepo - head repository that triggered the workflow (repo from which PR came)
+ * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
+ * @param sourceEventName - name of the event that triggered the workflow
+ *        (different than self event name for workflow_run)
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param notifyPRCancel - whether to notify in PRs about cancelling
+ * @param notifyPRMessageStart - whether to notify PRs when the action starts
+ * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
+ * @param skipEventTypes - array of event names that should be skipped
+ * @param reason - reason for cancelling
+ * @return array of canceled workflow run ids
+ */
+async function findAndCancelRuns(
+  repositoryInfo: RepositoryInfo,
+  selfRunId: number,
+  sourceWorkflowId: number | string,
+  sourceRunId: number,
+  headRepo: string,
+  headBranch: string,
+  sourceEventName: string,
+  cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
+  notifyPRCancel: boolean,
+  notifyPRMessageStart: string,
+  jobNameRegexps: string[],
+  skipEventTypes: string[],
+  reason: string
+): Promise<number[]> {
+  const statusValues = ['queued', 'in_progress']
+  const mySourceGroup: WorkflowRunSourceGroup = {
+    headBranch,
+    headRepo,
+    eventName: sourceEventName,
+    workflowId: sourceWorkflowId
+  }
+  const workflowRuns = await getWorkflowRunsMatchingCriteria(
+    repositoryInfo,
+    statusValues,
+    cancelMode,
+    sourceWorkflowId,
+    sourceRunId,
+    sourceEventName,
+    mySourceGroup
+  )
+  const mapOfWorkflowRunsCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
+    repositoryInfo,
+    headRepo,
+    cancelMode,
+    cancelFutureDuplicates,
+    sourceRunId,
+    jobNameRegexps,
+    skipEventTypes,
+    workflowRuns
+  )
+  return await cancelTheRunsPerGroup(
+    repositoryInfo,
+    mapOfWorkflowRunsCandidatesToCancel,
+    cancelMode,
+    cancelFutureDuplicates,
+    notifyPRCancel,
+    selfRunId,
+    reason
+  )
 }
 
+/**
+ * Returns environment variable that is required - throws error if it is not defined.
+ * @param key key for the env variable
+ * @return value of the env variable
+ */
+function getRequiredEnv(key: string): string {
+  const value = process.env[key]
+  if (value === undefined) {
+    const message = `${key} was not defined.`
+    throw new Error(message)
+  }
+  return value
+}
+
+/**
+ * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * @param repositoryInfo - information about the repository used
+ * @param runId - run id of the run to check
+ * @return information about the triggering workflow
+ */
 async function getOrigin(
-  octokit: github.GitHub,
-  runId: number,
-  owner: string,
-  repo: string
-): Promise<
-  [string, string, string, string, string, rest.PullsListResponseItem | null]
-> {
-  const reply = await octokit.actions.getWorkflowRun({
-    owner,
-    repo,
+  repositoryInfo: RepositoryInfo,
+  runId: number
+): Promise<TriggeringWorkflowInfo> {
+  const reply = await repositoryInfo.octokit.actions.getWorkflowRun({
+    owner: repositoryInfo.owner,
+    repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
     run_id: runId
   })
@@ -568,32 +993,50 @@ async function getOrigin(
   let pullRequest: rest.PullsListResponseItem | null = null
   if (sourceRun.event === 'pull_request') {
     pullRequest = await findPullRequest(
-      octokit,
-      owner,
-      repo,
+      repositoryInfo,
       sourceRun.head_repository.owner.login,
       sourceRun.head_branch,
       sourceRun.head_sha
     )
   }
 
-  return [
-    reply.data.head_repository.full_name,
-    reply.data.head_branch,
-    reply.data.event,
-    reply.data.head_sha,
-    pullRequest ? pullRequest.merge_commit_sha : '',
-    pullRequest
-  ]
+  return {
+    headRepo: reply.data.head_repository.full_name,
+    headBranch: reply.data.head_branch,
+    headSha: reply.data.head_sha,
+    mergeCommitSha: pullRequest ? pullRequest.merge_commit_sha : null,
+    pullRequest: pullRequest ? pullRequest : null,
+    eventName: reply.data.event
+  }
 }
 
+/**
+ * Performs the actual cancelling.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param selfRunId - number of own run id
+ * @param sourceWorkflowId - source workflow id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param sourceRunId - source run id that triggered the workflow
+ *        (might be different than self for workflow_run)
+ * @param headRepo - head repository that triggered the workflow (repo from which PR came)
+ * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
+ * @param sourceEventName - name of the event that triggered the workflow
+ *        (different than self event name for workflow_run)
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param notifyPRCancel - whether to notify in PRs about cancelling
+ * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
+ *        generated automatically)
+ * @param notifyPRMessageStart - whether to notify PRs when the action starts
+ * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
+ * @param skipEventTypes - array of event names that should be skipped
+ */
 async function performCancelJob(
-  octokit: github.GitHub,
+  repositoryInfo: RepositoryInfo,
   selfRunId: number,
   sourceWorkflowId: number | string,
   sourceRunId: number,
-  owner: string,
-  repo: string,
   headRepo: string,
   headBranch: string,
   sourceEventName: string,
@@ -609,7 +1052,7 @@ async function performCancelJob(
     '\n###################################################################################\n'
   )
   core.info(
-    `All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
+    `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
       `head repo ${headRepo}, headBranch: ${headBranch}, ` +
       `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
   )
@@ -636,9 +1079,14 @@ async function performCancelJob(
     reason = `It has jobs matching ${jobNameRegexps}.`
   } else if (cancelMode === CancelMode.DUPLICATES) {
     core.info(
-      `# Cancel duplicate runs started before ${sourceRunId} for workflow ${sourceWorkflowId}.`
+      `# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`
+    )
+    reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
+  } else if (cancelMode === CancelMode.ALL_DUPLICATES) {
+    core.info(
+      `# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`
     )
-    reason = `It in earlier duplicate of ${sourceWorkflowId} run.`
+    reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
   } else {
     throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`)
   }
@@ -647,12 +1095,10 @@ async function performCancelJob(
   )
 
   return await findAndCancelRuns(
-    octokit,
+    repositoryInfo,
     selfRunId,
     sourceWorkflowId,
     sourceRunId,
-    owner,
-    repo,
     headRepo,
     headBranch,
     sourceEventName,
@@ -666,11 +1112,165 @@ async function performCancelJob(
   )
 }
 
+/**
+ * Retrieves information about source workflow Id. Either from the current event or from the workflow
+ * nme specified. If the file name is not specified, the workflow to act on is either set to self event
+ * or in case of workflow_run event - to the workflow id that triggered the 'workflow_run' event.
+ *
+ * @param repositoryInfo - information about the repository used
+ * @param workflowFileName - optional workflow file name
+ * @param sourceRunId - source run id of the workfow
+ * @param selfRunId - self run id
+ * @return workflow id that is associate with the workflow we are going to act on.
+ */
+async function retrieveWorkflowId(
+  repositoryInfo: RepositoryInfo,
+  workflowFileName: string | null,
+  sourceRunId: number,
+  selfRunId: number
+): Promise<string | number> {
+  let sourceWorkflowId
+  if (workflowFileName) {
+    sourceWorkflowId = workflowFileName
+    core.info(
+      `\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`
+    )
+  } else {
+    sourceWorkflowId = await getWorkflowId(repositoryInfo, sourceRunId)
+    if (sourceRunId === selfRunId) {
+      core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
+    } else {
+      core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
+    }
+  }
+  return sourceWorkflowId
+}
+/**
+ * Sets output but also prints the output value in the logs.
+ *
+ * @param name name of the output
+ * @param value value of the output
+ */
 function verboseOutput(name: string, value: string): void {
   core.info(`Setting output: ${name}: ${value}`)
   core.setOutput(name, value)
 }
 
+/**
+ * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
+ * are verified and in case od unexpected combination found, approrpriate error is raised.
+ *
+ * @param eventName - name of the event to act on
+ * @param sourceRunId - run id of the triggering event
+ * @param selfRunId - our own run id
+ * @param cancelMode - cancel mode used
+ * @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
+ * @param jobNameRegexps - array of regular expression of job names
+ */
+function performSanityChecks(
+  eventName: string,
+  sourceRunId: number,
+  selfRunId: number,
+  cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
+  jobNameRegexps: string[]
+): void {
+  if (
+    eventName === 'workflow_run' &&
+    sourceRunId === selfRunId &&
+    cancelMode === CancelMode.DUPLICATES
+  ) {
+    throw Error(
+      `You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
+        'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
+        'See the docs for details.'
+    )
+  }
+
+  if (
+    jobNameRegexps.length > 0 &&
+    [
+      CancelMode.DUPLICATES,
+      CancelMode.SELF,
+      CancelMode.ALL_DUPLICATES
+    ].includes(cancelMode)
+  ) {
+    throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`)
+  }
+
+  if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+    throw Error(
+      `The  ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`
+    )
+  }
+}
+
+/**
+ * Produces basic outputs for the action. This does not include cancelled workflow run id - those are
+ * set after cancelling is done.
+ *
+ * @param triggeringWorkflowInfo
+ */
+function produceBasicOutputs(
+  triggeringWorkflowInfo: TriggeringWorkflowInfo
+): void {
+  verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo)
+  verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch)
+  verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha)
+  verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName)
+  verboseOutput(
+    'pullRequestNumber',
+    triggeringWorkflowInfo.pullRequest
+      ? triggeringWorkflowInfo.pullRequest.number.toString()
+      : ''
+  )
+  verboseOutput(
+    'mergeCommitSha',
+    triggeringWorkflowInfo.mergeCommitSha
+      ? triggeringWorkflowInfo.mergeCommitSha
+      : ''
+  )
+  verboseOutput(
+    'targetCommitSha',
+    triggeringWorkflowInfo.mergeCommitSha
+      ? triggeringWorkflowInfo.mergeCommitSha
+      : triggeringWorkflowInfo.headSha
+  )
+}
+
+/**
+ * Notifies the PR that the action has started.
+ *
+ * @param repositoryInfo information about the repository
+ * @param triggeringWorkflowInfo information about the triggering workflow
+ * @param sourceRunId run id of the source workflow
+ * @param selfRunId self run id
+ * @param notifyPRMessageStart whether to notify about the start of the action
+ */
+async function notifyActionStart(
+  repositoryInfo: RepositoryInfo,
+  triggeringWorkflowInfo: TriggeringWorkflowInfo,
+  sourceRunId: number,
+  selfRunId: number,
+  notifyPRMessageStart: string
+): Promise<void> {
+  if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+    const selfWorkflowRunUrl =
+      `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
+      `/actions/runs/${selfRunId}`
+    await repositoryInfo.octokit.issues.createComment({
+      owner: repositoryInfo.owner,
+      repo: repositoryInfo.repo,
+      // eslint-disable-next-line @typescript-eslint/camelcase
+      issue_number: triggeringWorkflowInfo.pullRequest.number,
+      body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
+    })
+  }
+}
+
+/**
+ * Main run method that does everything :)
+ */
 async function run(): Promise<void> {
   const token = core.getInput('token', {required: true})
   const octokit = new github.GitHub(token)
@@ -699,33 +1299,31 @@ async function run(): Promise<void> {
 
   const [owner, repo] = repository.split('/')
 
+  const repositoryInfo: RepositoryInfo = {
+    octokit,
+    owner,
+    repo
+  }
   core.info(
     `\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
       ` skipEventTypes: ${skipEventTypes}\n`
   )
-  let sourceWorkflowId
+  const sourceWorkflowId = await retrieveWorkflowId(
+    repositoryInfo,
+    workflowFileName,
+    sourceRunId,
+    selfRunId
+  )
+
+  performSanityChecks(
+    eventName,
+    sourceRunId,
+    selfRunId,
+    cancelMode,
+    cancelFutureDuplicates,
+    jobNameRegexps
+  )
 
-  if (workflowFileName) {
-    sourceWorkflowId = workflowFileName
-    core.info(
-      `\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`
-    )
-  } else {
-    sourceWorkflowId = await getWorkflowId(octokit, sourceRunId, owner, repo)
-    if (sourceRunId === selfRunId) {
-      core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
-    } else {
-      core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
-    }
-    if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
-      if (cancelMode === CancelMode.DUPLICATES)
-        throw Error(
-          `You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
-            'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
-            'See the docs for details.'
-        )
-    }
-  }
   core.info(
     `Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
       `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
@@ -733,54 +1331,25 @@ async function run(): Promise<void> {
       `jobNames: ${jobNameRegexps}`
   )
 
-  if (
-    jobNameRegexps.length > 0 &&
-    [CancelMode.DUPLICATES, CancelMode.SELF].includes(cancelMode)
-  ) {
-    throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`)
-  }
+  const triggeringWorkflowInfo = await getOrigin(repositoryInfo, sourceRunId)
+  produceBasicOutputs(triggeringWorkflowInfo)
 
-  const [
-    headRepo,
-    headBranch,
-    sourceEventName,
-    headSha,
-    mergeCommitSha,
-    pullRequest
-  ] = await getOrigin(octokit, sourceRunId, owner, repo)
-
-  verboseOutput('sourceHeadRepo', headRepo)
-  verboseOutput('sourceHeadBranch', headBranch)
-  verboseOutput('sourceHeadSha', headSha)
-  verboseOutput('sourceEvent', sourceEventName)
-  verboseOutput(
-    'pullRequestNumber',
-    pullRequest ? pullRequest.number.toString() : ''
+  await notifyActionStart(
+    repositoryInfo,
+    triggeringWorkflowInfo,
+    sourceRunId,
+    selfRunId,
+    notifyPRMessageStart
   )
-  verboseOutput('mergeCommitSha', mergeCommitSha)
-  verboseOutput('targetCommitSha', pullRequest ? mergeCommitSha : headSha)
-
-  const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`
-  if (notifyPRMessageStart && pullRequest) {
-    await octokit.issues.createComment({
-      owner,
-      repo,
-      // eslint-disable-next-line @typescript-eslint/camelcase
-      issue_number: pullRequest.number,
-      body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
-    })
-  }
 
   const cancelledRuns = await performCancelJob(
-    octokit,
+    repositoryInfo,
     selfRunId,
     sourceWorkflowId,
     sourceRunId,
-    owner,
-    repo,
-    headRepo,
-    headBranch,
-    sourceEventName,
+    triggeringWorkflowInfo.headRepo,
+    triggeringWorkflowInfo.headBranch,
+    triggeringWorkflowInfo.eventName,
     cancelMode,
     notifyPRCancel,
     notifyPRCancelMessage,


[airflow-cancel-workflow-runs] 23/44: Merge pull request #3 from n1hility/dependabot/npm_and_yarn/actions/http-client-1.0.8

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit de231f592ec85e392b4580d3ed3336922c024e52
Merge: 7d3eeff f4aa9bb
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Tue May 5 23:12:05 2020 -0500

    Merge pull request #3 from n1hility/dependabot/npm_and_yarn/actions/http-client-1.0.8
    
    Bump @actions/http-client from 1.0.4 to 1.0.8

 package-lock.json | 6 +++---
 1 file changed, 3 insertions(+), 3 deletions(-)



[airflow-cancel-workflow-runs] 13/44: optimize queries to only pull active statuses

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 0802ecf372e2ec66062812f10e6a1a7aa33b8eb3
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 18 23:03:34 2020 -0600

    optimize queries to only pull active statuses
---
 dist/index.js | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/dist/index.js b/dist/index.js
index 08cf9c8..5505475 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1547,7 +1547,7 @@ function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, eve
                 core.info(`Matched ${selfRunId}`);
             }
             if ('completed' === element.status.toString() ||
-                !['push', 'pull_request'].includes(element.event.toString)) {
+                !['push', 'pull_request'].includes(element.event.toString())) {
                 continue;
             }
             // This is a set of one in the non-schedule case, otherwise everything is a candidate


[airflow-cancel-workflow-runs] 05/44: Reenable branch filtering

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 24356dc8cce3420533d5b0fb5b1eea5ba60d22bc
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 4 03:35:49 2020 -0600

    Reenable branch filtering
---
 dist/index.js | 2 +-
 src/main.ts   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 3995113..1d02837 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1495,7 +1495,7 @@ function run() {
             const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
                 owner,
                 repo,
-                // branch,
+                branch,
                 event: 'push'
             });
             let matched = false;
diff --git a/src/main.ts b/src/main.ts
index bfe9c55..12547c2 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -37,7 +37,7 @@ async function run(): Promise<void> {
     const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
       owner,
       repo,
-      // branch,
+      branch,
       event: 'push'
     })
 


[airflow-cancel-workflow-runs] 41/44: Adds self-preservation mechanism. (#12)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f06d03cd576a179ea5169d048dbd8c8d73757b52
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 17:41:54 2020 +0100

    Adds self-preservation mechanism. (#12)
    
    This mechanism is useful in some edge-cases, especially when you
    re-run a build and you base the cancelling on named jobs, the
    jobs might get the name in the previous run and when re-running,
    the action might cancel its own run.
---
 README.md     |  8 ++++++++
 action.yml    |  5 +++++
 dist/index.js | 27 ++++++++++++++++++---------
 src/main.ts   | 34 +++++++++++++++++++++++++++-------
 4 files changed, 58 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index b212dd9..5f98862 100644
--- a/README.md
+++ b/README.md
@@ -115,6 +115,7 @@ If you want a comprehensive solution, you should use the action as follows:
 | `notifyPRMessageStart`   | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
 | `jobNameRegexps`         | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` and `allDuplicateNamedJobs` modes.     |
 | `skipEventTypes`         | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
+| `selfPreservation`       | no       | true         | Do not cancel self.                                                                                                                                                                                              |
 | `workflowFileName`       | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
 
 
@@ -566,6 +567,11 @@ match.
 Note that the match must be identical. If there are two jobs that have a different Branch
 they will both match the same pattern, but they are not considered duplicates.
 
+Also, this is one of the jobs It has also self-preservation turned off.
+This means that in case the job determines that it is itself a duplicate it will cancel itself. That's
+why checking for duplicates of self-workflow should be the last step in the cancelling process.
+
+
 ```yaml
 on:
   push:
@@ -584,7 +590,9 @@ jobs:
           cancelMode: allDuplicatedNamedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
           jobNameRegexps: '["Branch: .* Repo: .* Event: .* "]'
+          selfPreservation: false
           notifyPRCancel: true
+
 ```
 
 
diff --git a/action.yml b/action.yml
index 541094a..f678dc6 100644
--- a/action.yml
+++ b/action.yml
@@ -46,6 +46,11 @@ inputs:
       In case of duplicate canceling, cancel also future duplicates leaving only the "freshest" running
       job and not all the future jobs. By default it is set to true.
     required: false
+  selfPreservation:
+    description: |
+      Do not cancel your own run. There are cases where selfPreservation should be disabled but it is
+      enabled by default. You can disable it by setting 'false' as value.
+    required: false
   jobNameRegexps:
     description: |
       Array of job name regexps (JSON-encoded string). Used by `failedJobs` and `namedJobs` cancel modes
diff --git a/dist/index.js b/dist/index.js
index 2a806db..09fdb5d 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -2013,17 +2013,23 @@ function findPullRequestForRunItem(repositoryInfo, runItem) {
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
+ * @param selfPreservation - whether the run will cancel itself if requested
+ * @param selfRunId - my own run id
  * @param workflowRuns - map of workflow runs found
  * @parm maps with string key and array of run items as value. The key might be
  *       * source group id (allDuplicates mode)
  *       * matching job name (allDuplicatedMatchingJobNames mode)
  */
-function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns) {
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns) {
     return __awaiter(this, void 0, void 0, function* () {
         const mapOfWorkflowRunCandidates = new Map();
         for (const [key, runItem] of workflowRuns) {
             core.info(`\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
                 ` Created at ${runItem.created_at}\n`);
+            if (runItem.id === selfRunId && selfPreservation) {
+                core.info(`\nI have self-preservation built in. I refuse to cancel myself :)\n`);
+                continue;
+            }
             yield checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
         }
         return mapOfWorkflowRunCandidates;
@@ -2083,7 +2089,7 @@ function cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel,
                     yield notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason);
                 }
             }
-            core.info(`\nCancelling run: ${runItem}.\n`);
+            core.info(`\nCancelling run: ${runItem.id}.\n`);
             yield cancelRun(repositoryInfo, runItem.id);
             cancelledRuns.push(runItem.id);
         }
@@ -2143,13 +2149,14 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandida
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
  * @param reason - reason for cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  * @return array of canceled workflow run ids
  */
-function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
         const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo);
-        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns);
+        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, selfRunId, selfPreservation, workflowRuns);
         return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
     });
 }
@@ -2207,15 +2214,16 @@ function getTriggeringRunInfo(repositoryInfo, runId) {
  * @param selfRunId - number of own run id
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
- * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
  * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
  *        generated automatically)
  * @param notifyPRMessageStart - whether to notify PRs when the action starts
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  */
-function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
+function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
@@ -2259,7 +2267,7 @@ function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMo
             throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason, selfPreservation);
     });
 }
 /**
@@ -2304,7 +2312,7 @@ function verboseOutput(name, value) {
 }
 /**
  * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
- * are verified and in case od unexpected combination found, approrpriate error is raised.
+ * are verified and in case od unexpected combination found, appropriate error is raised.
  *
  * @param eventName - name of the event to act on
  * @param runId - run id of the triggering event
@@ -2401,6 +2409,7 @@ function run() {
         const notifyPRMessageStart = core.getInput('notifyPRMessageStart');
         const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
         const jobNameRegexpsString = core.getInput('jobNameRegexps');
+        const selfPreservation = (core.getInput('selfPreservation') || 'true').toLowerCase() === 'true';
         const cancelFutureDuplicates = (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true';
         const jobNameRegexps = jobNameRegexpsString
             ? JSON.parse(jobNameRegexpsString)
@@ -2427,7 +2436,7 @@ function run() {
         const triggeringRunInfo = yield getTriggeringRunInfo(repositoryInfo, sourceRunId);
         produceBasicOutputs(triggeringRunInfo);
         yield notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart);
-        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
+        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates, selfPreservation);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index abf39f1..3c4eab1 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -790,6 +790,8 @@ async function findPullRequestForRunItem(
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
+ * @param selfPreservation - whether the run will cancel itself if requested
+ * @param selfRunId - my own run id
  * @param workflowRuns - map of workflow runs found
  * @parm maps with string key and array of run items as value. The key might be
  *       * source group id (allDuplicates mode)
@@ -802,6 +804,8 @@ async function filterAndMapWorkflowRunsToGroups(
   cancelFutureDuplicates: boolean,
   jobNameRegexps: string[],
   skipEventTypes: string[],
+  selfRunId: number,
+  selfPreservation: boolean,
   workflowRuns: Map<
     number,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
@@ -815,6 +819,12 @@ async function filterAndMapWorkflowRunsToGroups(
       `\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
         ` Created at ${runItem.created_at}\n`
     )
+    if (runItem.id === selfRunId && selfPreservation) {
+      core.info(
+        `\nI have self-preservation built in. I refuse to cancel myself :)\n`
+      )
+      continue
+    }
     await checkCandidateForCancelling(
       repositoryInfo,
       runItem,
@@ -908,7 +918,7 @@ async function cancelAllRunsInTheGroup(
         await notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason)
       }
     }
-    core.info(`\nCancelling run: ${runItem}.\n`)
+    core.info(`\nCancelling run: ${runItem.id}.\n`)
     await cancelRun(repositoryInfo, runItem.id)
     cancelledRuns.push(runItem.id)
   }
@@ -999,6 +1009,7 @@ async function cancelTheRunsPerGroup(
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
  * @param reason - reason for cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  * @return array of canceled workflow run ids
  */
 async function findAndCancelRuns(
@@ -1011,7 +1022,8 @@ async function findAndCancelRuns(
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
   skipEventTypes: string[],
-  reason: string
+  reason: string,
+  selfPreservation: boolean
 ): Promise<number[]> {
   const statusValues = ['queued', 'in_progress']
   const workflowRuns = await getWorkflowRunsMatchingCriteria(
@@ -1027,6 +1039,8 @@ async function findAndCancelRuns(
     cancelFutureDuplicates,
     jobNameRegexps,
     skipEventTypes,
+    selfRunId,
+    selfPreservation,
     workflowRuns
   )
   return await cancelTheRunsPerGroup(
@@ -1105,13 +1119,14 @@ async function getTriggeringRunInfo(
  * @param selfRunId - number of own run id
  * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
- * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
  * @param notifyPRCancelMessage - message to send when cancelling the PR (overrides default message
  *        generated automatically)
  * @param notifyPRMessageStart - whether to notify PRs when the action starts
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
+ * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
+ * @param selfPreservation - whether the run will cancel itself if requested
  */
 async function performCancelJob(
   repositoryInfo: RepositoryInfo,
@@ -1123,7 +1138,8 @@ async function performCancelJob(
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
   skipEventTypes: string[],
-  cancelFutureDuplicates: boolean
+  cancelFutureDuplicates: boolean,
+  selfPreservation: boolean
 ): Promise<number[]> {
   core.info(
     '\n###################################################################################\n'
@@ -1192,7 +1208,8 @@ async function performCancelJob(
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    reason
+    reason,
+    selfPreservation
   )
 }
 
@@ -1242,7 +1259,7 @@ function verboseOutput(name: string, value: string): void {
 
 /**
  * Performs sanity check of the parameters passed. Some of the parameter combinations do not work so they
- * are verified and in case od unexpected combination found, approrpriate error is raised.
+ * are verified and in case od unexpected combination found, appropriate error is raised.
  *
  * @param eventName - name of the event to act on
  * @param runId - run id of the triggering event
@@ -1380,6 +1397,8 @@ async function run(): Promise<void> {
   const notifyPRMessageStart = core.getInput('notifyPRMessageStart')
   const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId
   const jobNameRegexpsString = core.getInput('jobNameRegexps')
+  const selfPreservation =
+    (core.getInput('selfPreservation') || 'true').toLowerCase() === 'true'
   const cancelFutureDuplicates =
     (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true'
   const jobNameRegexps = jobNameRegexpsString
@@ -1449,7 +1468,8 @@ async function run(): Promise<void> {
     notifyPRMessageStart,
     jobNameRegexps,
     skipEventTypes,
-    cancelFutureDuplicates
+    cancelFutureDuplicates,
+    selfPreservation
   )
 
   verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))


[airflow-cancel-workflow-runs] 30/44: Merge pull request #2 from TobKed/Fix-example

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 70a45868a57b8d47a1ec696865d49a6ddce5b74c
Merge: 2210b4b 7dc5741
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Aug 29 12:42:49 2020 +0200

    Merge pull request #2 from TobKed/Fix-example
    
    Fix example

 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)


[airflow-cancel-workflow-runs] 42/44: Revert problem with non-iterable data.jobs sometimes.

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 169f1694e07cdfff421539599c048cdc276e2a00
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 18:29:19 2020 +0100

    Revert problem with non-iterable data.jobs sometimes.
    
    The data.jobs is apparently deprecated but getting rid of it
    causes data not iterable sometimes. Before investigation we want to
    revert that to stop failures from happening
---
 dist/index.js | 2 +-
 src/main.ts   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 09fdb5d..a46b432 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1627,7 +1627,7 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
         try {
             for (var _b = __asyncValues(repositoryInfo.octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
                 const item = _c.value;
-                for (const job of item.data) {
+                for (const job of item.data.jobs) {
                     core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`);
                     const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps);
                     if (jobMatched) {
diff --git a/src/main.ts b/src/main.ts
index 3c4eab1..7ee681b 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -228,7 +228,7 @@ async function jobsMatchingNames(
   const allMatches: string[] = []
   let matched = false
   for await (const item of repositoryInfo.octokit.paginate.iterator(listJobs)) {
-    for (const job of item.data) {
+    for (const job of item.data.jobs) {
       core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`)
       const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps)
       if (jobMatched) {


[airflow-cancel-workflow-runs] 31/44: Add workflow_dispatch event to cancellable runs (#3)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f0545153d506a7e63e2494d0113695f67f1a260e
Author: Tobiasz Kędzierski <to...@gmail.com>
AuthorDate: Sat Aug 29 13:47:05 2020 +0200

    Add workflow_dispatch event to cancellable runs (#3)
---
 dist/index.js | 2 +-
 src/main.ts   | 2 +-
 2 files changed, 2 insertions(+), 2 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 2a8c75f..2da1e6e 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1466,7 +1466,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
 const treemap = __importStar(__webpack_require__(706));
-const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule'];
+const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule', 'workflow_dispatch'];
 var CancelMode;
 (function (CancelMode) {
     CancelMode["DUPLICATES"] = "duplicates";
diff --git a/src/main.ts b/src/main.ts
index 1acb58a..c4ee4da 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -3,7 +3,7 @@ import * as core from '@actions/core'
 import * as rest from '@octokit/rest'
 import * as treemap from 'jstreemap'
 
-const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule']
+const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule', 'workflow_dispatch']
 
 enum CancelMode {
   DUPLICATES = 'duplicates',


[airflow-cancel-workflow-runs] 37/44: Adds cancelling workflows by name (#8)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 0acb1c01f6740dfbca6eab6e21a5b5066e8bafb3
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Tue Oct 20 08:14:31 2020 +0200

    Adds cancelling workflows by name (#8)
---
 README.md     | 282 ++++++++++++++++++++++++++++++++--------------------------
 action.yml    |   5 ++
 dist/index.js |  34 ++++---
 src/main.ts   |  55 ++++++------
 4 files changed, 210 insertions(+), 166 deletions(-)

diff --git a/README.md b/README.md
index 5354a8e..90c9a22 100644
--- a/README.md
+++ b/README.md
@@ -15,11 +15,6 @@
   - [Inputs](#inputs)
   - [Outputs](#outputs)
 - [Examples](#examples)
-  - [Repositories that do not use Pull Requests from forks](#repositories-that-do-not-use-pull-requests-from-forks)
-    - [Cancel duplicate runs for "self" workflow](#cancel-duplicate-runs-for-self-workflow)
-    - [Cancel "self" workflow run](#cancel-self-workflow-run)
-    - [Fail-fast workflow runs with failed jobs](#fail-fast-workflow-runs-with-failed-jobs)
-    - [Cancel all runs with named jobs](#cancel-all-runs-with-named-jobs)
   - [Repositories that use Pull Requests from forks](#repositories-that-use-pull-requests-from-forks)
     - [Cancel duplicate runs for the source workflow](#cancel-duplicate-runs-for-the-source-workflow)
     - [Cancel duplicate jobs for triggered workflow](#cancel-duplicate-jobs-for-triggered-workflow)
@@ -28,6 +23,12 @@
     - [Fail-fast source workflow runs with failed jobs](#fail-fast-source-workflow-runs-with-failed-jobs)
     - [Fail-fast source workflow runs with failed jobs and corresponding triggered runs](#fail-fast-source-workflow-runs-with-failed-jobs-and-corresponding-triggered-runs)
     - [Fail-fast for triggered workflow runs with failed jobs](#fail-fast-for-triggered-workflow-runs-with-failed-jobs)
+    - [Cancel another workflow run](#cancel-another-workflow-run)
+  - [Repositories that do not use Pull Requests from forks](#repositories-that-do-not-use-pull-requests-from-forks)
+    - [Cancel duplicate runs for "self" workflow](#cancel-duplicate-runs-for-self-workflow)
+    - [Cancel "self" workflow run](#cancel-self-workflow-run)
+    - [Fail-fast workflow runs with failed jobs](#fail-fast-workflow-runs-with-failed-jobs)
+    - [Cancel all runs with named jobs](#cancel-all-runs-with-named-jobs)
   - [Development environment](#development-environment)
   - [License](#license)
 
@@ -114,7 +115,8 @@ and `schedule` events are no longer needed.
 | `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
 | `notifyPRMessageStart`  | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
 | `jobNameRegexps`        | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
-| `skipEventTypes`        | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events..                                                       |
+| `skipEventTypes`        | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
+| `workflowFileName`      | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
 
 
 The job cancel modes work as follows:
@@ -145,127 +147,6 @@ The job cancel modes work as follows:
 Note that you can combine the steps below in several steps of the same job. The examples here are showing
 one step per case for clarity.
 
-## Repositories that do not use Pull Requests from forks
-
-Note that examples in this chapter only work if you do not have Pull Requests coming from forks (so for
-example if you only work in a private repository). When those action runs within the usual `pull_request`
-triggered runs coming from a fork, they have not enough permissions to cancel running workflows.
-
-If you want to cancel `pull_requests` from forks, you need to use `workflow_run` triggered runs - see the
-[Repositories that use Pull Requests from fork](#repositories-that-use-pull-requests-from-forks) chapter.
-
-Note that in case you configure the separate `workflow_run` Cancelling workflow, there is no need to add
-the action to the "source" workflows. The "Canceling workflow" pattern handles well not only Pull Requests
-from the forks, but also all other cases - including cancelling Pull Requests for the same repository
-and canceling scheduled runs.
-
-### Cancel duplicate runs for "self" workflow
-
-Cancels past runs for the same workflow (with the same branch).
-
-In the case below, any of the direct "push" events will cancel all past runs for the same branch as the
-one being pushed. However, it can be configured for "pull_request" (in the same repository) or "schedule"
-type of events as well. It will also notify the PR with the comment containining why it has been
-cancelled.
-
-```yaml
-name: CI
-on: push
-jobs:
-  cancel-duplicate-workflow-runs:
-    name: "Cancel duplicate workflow runs"
-    runs-on: ubuntu-latest
-    steps:
-      - uses: potiuk/cancel-workflow-runs@v2
-        name: "Cancel duplicate workflow runs"
-        with:
-          cancelMode: duplicates
-          notifyPRCancel: true
-```
-
-### Cancel "self" workflow run
-
-This is useful in case you decide to cancel "self" run.
-
-In the case below - own workflow will be cancelled immediately. It can be configured for "push",
-"pull_request" (from the same repository) or "schedule" type of events.
-
-```yaml
-name: CI
-on: push
-jobs:
-  cancel-self-run:
-    name: "Cancel the self workflow run"
-    runs-on: ubuntu-latest
-    steps:
-      - name: "Cancel the self workflow run"
-        uses: potiuk/cancel-workflow-runs@v2
-        with:
-          cancelMode: self
-          token: ${{ secrets.GITHUB_TOKEN }}
-          notifyPRCancel: true
-```
-
-### Fail-fast workflow runs with failed jobs
-
-Cancels all runs (including self run!) if they have failed jobs matching any of the regular expressions.
-Note that it does not take into account the branch of the running jobs. It will cancel all runs with failed
-jobs, no matter what branch originated it.
-
-In the case below, if any of the own workflow runs have failed jobs matching any of the
-`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
-
-```yaml
-name: CI
-on:
-  push:
-
-jobs:
-  cancel-self-failed-runs:
-    name: "Cancel failed runs"
-    runs-on: ubuntu-latest
-    steps:
-      - uses: potiuk/cancel-workflow-runs@v2
-        name: "Cancel failed runs"
-        with:
-          cancelMode: failedJobs
-          token: ${{ secrets.GITHUB_TOKEN }}
-          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
-          notifyPRCancel: true
-```
-
-### Cancel all runs with named jobs
-
-Cancels all runs (including self run!) if any of the job names match any of the regular
-expressions. Note that it does not take into account the branch of the runs. It will cancel all runs with
-matching jobs, no matter what branch originated it.
-
-This is useful in case of job names generated dynamically.
-
-In the case below, if any of the "self" workflow runs has job names that matches any of the
-`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
-
-```yaml
-on:
-  push:
-  workflow_run:
-    workflows: ['CI']
-    types: ['requested']
-
-jobs:
-  cancel-self-failed-runs:
-    name: "Cancel the self workflow run"
-    runs-on: ubuntu-latest
-    steps:
-      - uses: potiuk/cancel-workflow-runs@v2
-        name: "Cancel past CI runs"
-        with:
-          cancelMode: namedJobs
-          token: ${{ secrets.GITHUB_TOKEN }}
-          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
-          notifyPRCancel: true
-```
-
 ## Repositories that use Pull Requests from forks
 
 Note that in case you implement separate "Canceling workflow", following the examples below, you do not
@@ -611,6 +492,153 @@ jobs:
           jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
 ```
 
+### Cancel another workflow run
+
+This is useful in case you decide to cancel the *source run* that triggered the *triggered run*.
+In the case below, the step cancels the `CI` workflow that triggered the `Cancelling` run.
+
+```yaml
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+  cancel-other-workflow-run:
+    name: "Cancel the self CI workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - name: "Cancel the self CI workflow run"
+        uses: potiuk/cancel-workflow-runs@v2
+        with:
+          cancelMode: duplicates
+          token: ${{ secrets.GITHUB_TOKEN }}
+          workflowFileName: other_workflow.yml
+```
+
+
+
+## Repositories that do not use Pull Requests from forks
+
+Note that examples in this chapter only work if you do not have Pull Requests coming from forks (so for
+example if you only work in a private repository). When those action runs within the usual `pull_request`
+triggered runs coming from a fork, they have not enough permissions to cancel running workflows.
+
+If you want to cancel `pull_requests` from forks, you need to use `workflow_run` triggered runs - see the
+[Repositories that use Pull Requests from fork](#repositories-that-use-pull-requests-from-forks) chapter.
+
+Note that in case you configure the separate `workflow_run` Cancelling workflow, there is no need to add
+the action to the "source" workflows. The "Canceling workflow" pattern handles well not only Pull Requests
+from the forks, but also all other cases - including cancelling Pull Requests for the same repository
+and canceling scheduled runs.
+
+### Cancel duplicate runs for "self" workflow
+
+Cancels past runs for the same workflow (with the same branch).
+
+In the case below, any of the direct "push" events will cancel all past runs for the same branch as the
+one being pushed. However, it can be configured for "pull_request" (in the same repository) or "schedule"
+type of events as well. It will also notify the PR with the comment containining why it has been
+cancelled.
+
+```yaml
+name: CI
+on: push
+jobs:
+  cancel-duplicate-workflow-runs:
+    name: "Cancel duplicate workflow runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel duplicate workflow runs"
+        with:
+          cancelMode: duplicates
+          notifyPRCancel: true
+```
+
+### Cancel "self" workflow run
+
+This is useful in case you decide to cancel "self" run.
+
+In the case below - own workflow will be cancelled immediately. It can be configured for "push",
+"pull_request" (from the same repository) or "schedule" type of events.
+
+```yaml
+name: CI
+on: push
+jobs:
+  cancel-self-run:
+    name: "Cancel the self workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - name: "Cancel the self workflow run"
+        uses: potiuk/cancel-workflow-runs@v2
+        with:
+          cancelMode: self
+          token: ${{ secrets.GITHUB_TOKEN }}
+          notifyPRCancel: true
+```
+
+### Fail-fast workflow runs with failed jobs
+
+Cancels all runs (including self run!) if they have failed jobs matching any of the regular expressions.
+Note that it does not take into account the branch of the running jobs. It will cancel all runs with failed
+jobs, no matter what branch originated it.
+
+In the case below, if any of the own workflow runs have failed jobs matching any of the
+`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
+
+```yaml
+name: CI
+on:
+  push:
+
+jobs:
+  cancel-self-failed-runs:
+    name: "Cancel failed runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel failed runs"
+        with:
+          cancelMode: failedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+          notifyPRCancel: true
+```
+
+### Cancel all runs with named jobs
+
+Cancels all runs (including self run!) if any of the job names match any of the regular
+expressions. Note that it does not take into account the branch of the runs. It will cancel all runs with
+matching jobs, no matter what branch originated it.
+
+This is useful in case of job names generated dynamically.
+
+In the case below, if any of the "self" workflow runs has job names that matches any of the
+`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
+
+```yaml
+on:
+  push:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-self-failed-runs:
+    name: "Cancel the self workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel past CI runs"
+        with:
+          cancelMode: namedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+          notifyPRCancel: true
+```
+
 
 ## Development environment
 
diff --git a/action.yml b/action.yml
index 8fc9087..103d4e4 100644
--- a/action.yml
+++ b/action.yml
@@ -46,6 +46,11 @@ inputs:
       Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used
       in order to skip direct pushes or scheduled events.
     required: false
+  workflowFileName:
+    description: |
+      Name of the workflow file. It can be used if you want to cancel a different workflow than yours.
+    required: false
+
 runs:
   using: 'node12'
   main: 'dist/index.js'
diff --git a/dist/index.js b/dist/index.js
index 53c47fc..a347a2c 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1894,30 +1894,38 @@ function run() {
         const skipEventTypes = skipEventTypesString
             ? JSON.parse(skipEventTypesString)
             : [];
+        const workflowFileName = core.getInput('workflowFileName');
         const [owner, repo] = repository.split('/');
         core.info(`\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
             ` skipEventTypes: ${skipEventTypes}\n`);
-        const sourceWorkflowId = yield getWorkflowId(octokit, sourceRunId, owner, repo);
+        let sourceWorkflowId;
+        if (workflowFileName) {
+            sourceWorkflowId = workflowFileName;
+            core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`);
+        }
+        else {
+            sourceWorkflowId = yield getWorkflowId(octokit, sourceRunId, owner, repo);
+            if (sourceRunId === selfRunId) {
+                core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
+            }
+            else {
+                core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
+            }
+            if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
+                if (cancelMode === CancelMode.DUPLICATES)
+                    throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
+                        'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
+                        'See the docs for details.');
+            }
+        }
         core.info(`Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
             `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
             `sourceWorkflowId: ${sourceWorkflowId}, sourceRunId: ${sourceRunId}, selfRunId: ${selfRunId}, ` +
             `jobNames: ${jobNameRegexps}`);
-        if (sourceRunId === selfRunId) {
-            core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
-        }
-        else {
-            core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
-        }
         if (jobNameRegexps.length > 0 &&
             [CancelMode.DUPLICATES, CancelMode.SELF].includes(cancelMode)) {
             throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
         }
-        if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
-            if (cancelMode === CancelMode.DUPLICATES)
-                throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
-                    'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
-                    'See the docs for details.');
-        }
         const [headRepo, headBranch, sourceEventName, headSha, mergeCommitSha, pullRequest] = yield getOrigin(octokit, sourceRunId, owner, repo);
         verboseOutput('sourceHeadRepo', headRepo);
         verboseOutput('sourceHeadBranch', headBranch);
diff --git a/src/main.ts b/src/main.ts
index e7bcbe8..9f60f3d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -23,7 +23,7 @@ function createListRunsQueryOtherRuns(
   owner: string,
   repo: string,
   status: string,
-  workflowId: number,
+  workflowId: number | string,
   headBranch: string,
   eventName: string
 ): rest.RequestOptions {
@@ -44,7 +44,7 @@ function createListRunsQueryMyOwnRun(
   owner: string,
   repo: string,
   status: string,
-  workflowId: number,
+  workflowId: number | string,
   runId: number
 ): rest.RequestOptions {
   const request = {
@@ -64,7 +64,7 @@ function createListRunsQueryAllRuns(
   owner: string,
   repo: string,
   status: string,
-  workflowId: number
+  workflowId: number | string
 ): rest.RequestOptions {
   const request = {
     owner,
@@ -330,7 +330,7 @@ async function cancelRun(
 async function findAndCancelRuns(
   octokit: github.GitHub,
   selfRunId: number,
-  sourceWorkflowId: number,
+  sourceWorkflowId: number | string,
   sourceRunId: number,
   owner: string,
   repo: string,
@@ -569,7 +569,7 @@ async function getOrigin(
 async function performCancelJob(
   octokit: github.GitHub,
   selfRunId: number,
-  sourceWorkflowId: number,
+  sourceWorkflowId: number | string,
   sourceRunId: number,
   owner: string,
   repo: string,
@@ -670,6 +670,7 @@ async function run(): Promise<void> {
   const skipEventTypes = skipEventTypesString
     ? JSON.parse(skipEventTypesString)
     : []
+  const workflowFileName = core.getInput('workflowFileName')
 
   const [owner, repo] = repository.split('/')
 
@@ -677,12 +678,29 @@ async function run(): Promise<void> {
     `\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo},` +
       ` skipEventTypes: ${skipEventTypes}\n`
   )
-  const sourceWorkflowId = await getWorkflowId(
-    octokit,
-    sourceRunId,
-    owner,
-    repo
-  )
+  let sourceWorkflowId
+
+  if (workflowFileName) {
+    sourceWorkflowId = workflowFileName
+    core.info(
+      `\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`
+    )
+  } else {
+    sourceWorkflowId = await getWorkflowId(octokit, sourceRunId, owner, repo)
+    if (sourceRunId === selfRunId) {
+      core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
+    } else {
+      core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
+    }
+    if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
+      if (cancelMode === CancelMode.DUPLICATES)
+        throw Error(
+          `You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
+            'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
+            'See the docs for details.'
+        )
+    }
+  }
   core.info(
     `Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
       `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
@@ -690,12 +708,6 @@ async function run(): Promise<void> {
       `jobNames: ${jobNameRegexps}`
   )
 
-  if (sourceRunId === selfRunId) {
-    core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
-  } else {
-    core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
-  }
-
   if (
     jobNameRegexps.length > 0 &&
     [CancelMode.DUPLICATES, CancelMode.SELF].includes(cancelMode)
@@ -703,15 +715,6 @@ async function run(): Promise<void> {
     throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`)
   }
 
-  if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
-    if (cancelMode === CancelMode.DUPLICATES)
-      throw Error(
-        `You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
-          'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
-          'See the docs for details.'
-      )
-  }
-
   const [
     headRepo,
     headBranch,


[airflow-cancel-workflow-runs] 32/44: Fix pre commit failing on modified ts code (#4)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f6b43a668c2a4743ab96d1f7da91b30b0240de14
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sat Aug 29 14:10:50 2020 +0200

    Fix pre commit failing on modified ts code (#4)
    
    * The npm release step will not add generated files automaticaly
    
    Previously the release step was adding the generated dist
    files to git automatically, which was not a good idea because
    pre-commit did not fail in case someone modified the .ts file
    
    Now the pre-commit check for npm will behave like all other
    pre-commit checks - if a file will be modified by the check,
    the file will not be added and pre-commit check will fail
    allowing the author to add the file manually.
---
 dist/index.js | 8 +++++++-
 package.json  | 2 +-
 src/main.ts   | 8 +++++++-
 3 files changed, 15 insertions(+), 3 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index 2da1e6e..3c03be4 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1466,7 +1466,13 @@ Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
 const treemap = __importStar(__webpack_require__(706));
-const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule', 'workflow_dispatch'];
+const CANCELLABLE_RUNS = [
+    'push',
+    'pull_request',
+    'workflow_run',
+    'schedule',
+    'workflow_dispatch'
+];
 var CancelMode;
 (function (CancelMode) {
     CancelMode["DUPLICATES"] = "duplicates";
diff --git a/package.json b/package.json
index e739d21..17a13dc 100644
--- a/package.json
+++ b/package.json
@@ -12,7 +12,7 @@
     "pack": "ncc build",
     "test": "jest",
     "all": "npm run build && npm run format && npm run lint && npm run pack && npm test",
-    "release": "ncc build -o dist src/main.ts && git add -f dist/"
+    "release": "ncc build -o dist src/main.ts"
   },
   "repository": {
     "type": "git",
diff --git a/src/main.ts b/src/main.ts
index c4ee4da..01a8a9d 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -3,7 +3,13 @@ import * as core from '@actions/core'
 import * as rest from '@octokit/rest'
 import * as treemap from 'jstreemap'
 
-const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule', 'workflow_dispatch']
+const CANCELLABLE_RUNS = [
+  'push',
+  'pull_request',
+  'workflow_run',
+  'schedule',
+  'workflow_dispatch'
+]
 
 enum CancelMode {
   DUPLICATES = 'duplicates',


[airflow-cancel-workflow-runs] 07/44: Add support for pull requests

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 40bee3e7736ef97b456abb188f3e8a027aa48cce
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Thu Feb 6 13:22:34 2020 -0600

    Add support for pull requests
---
 __tests__/main.test.ts | 15 +++++++++++----
 src/main.ts            | 27 +++++++++++++++++----------
 2 files changed, 28 insertions(+), 14 deletions(-)

diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index efa2802..e237067 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -7,10 +7,17 @@ test('no op', () => {})
 // shows how the runner will run a javascript action with env / stdout protocol
 // test('test runs', () => {
 //   const ip = path.join(__dirname, '..', 'lib', 'main.js')
-//   process.env['INPUT_TOKEN'] = ''
-//   process.env['GITHUB_RUN_ID'] = '33782469'
-//   process.env['GITHUB_REPOSITORY'] = 'n1hility/cancel-previous-runs'
-//   process.env['GITHUB_REF'] = 'refs/heads/master'
+//     process.env['INPUT_TOKEN'] = ''
+//     process.env['GITHUB_RUN_ID'] = '35588693' //'33782469'
+//     process.env['GITHUB_REPOSITORY'] = ''
+//     process.env['GITHUB_HEAD_REF'] = 'refs/heads/n1hility-patch-5'
+//     process.env['GITHUB_EVENT_NAME'] = 'pull_request'
+
+// //   process.env['GITHUB_RUN_ID'] = '35599067'
+// //   process.env['GITHUB_REPOSITORY'] = ''
+// //   process.env['GITHUB_REF'] = 'refs/heads/master'
+// //   process.env['GITHUB_EVENT_NAME'] = 'push'
+
 //   const options: cp.ExecSyncOptions = {
 //     env: process.env
 //   }
diff --git a/src/main.ts b/src/main.ts
index 12547c2..c99a264 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -13,13 +13,15 @@ async function run(): Promise<void> {
     const branchPrefix = 'refs/heads/'
     const tagPrefix = 'refs/tags/'
 
-    if (eventName !== 'push') {
-      core.info('Skipping non-push event')
+    if (!['push', 'pull_request'].includes(eventName)) {
+      core.info('Skipping unsupported event')
       return
     }
 
-    let branch = getRequiredEnv('GITHUB_REF')
-    if (!branch.startsWith(branchPrefix)) {
+    const pullRequest = 'pull_request' === eventName
+
+    let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF')
+    if (!pullRequest && !branch.startsWith(branchPrefix)) {
       if (branch.startsWith(tagPrefix)) {
         core.info(`Skipping tag build`)
         return
@@ -38,16 +40,19 @@ async function run(): Promise<void> {
       owner,
       repo,
       branch,
-      event: 'push'
+      event: pullRequest ? 'pull_request' : 'push'
     })
 
     let matched = false
     let workflow = ''
-    let count = 0
+    let headRepoName = ''
     for await (const item of octokit.paginate.iterator(listRuns)) {
       // There is some sort of bug where the pagination URLs point to a
-      // different URL with a different data format
-      const elements = ++count < 2 ? item.data : item.data.workflow_runs
+      // different endpoint URL which trips up the resulting representation
+      // In that case, fallback to the actual REST 'workflow_runs' property
+      const elements =
+        item.data.length === undefined ? item.data.workflow_runs : item.data
+
       for (const element of elements) {
         core.info(
           `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
@@ -57,6 +62,7 @@ async function run(): Promise<void> {
           if (element.id.toString() === selfRunId) {
             matched = true
             workflow = element.workflow_url
+            headRepoName = pullRequest ? element.head_repository.full_name : ''
           }
           // Skip everything up to and matching this run
           continue
@@ -65,9 +71,10 @@ async function run(): Promise<void> {
         // Only cancel jobs with the same workflow
         if (
           workflow === element.workflow_url &&
-          element.status.toString() !== 'completed'
+          element.status.toString() !== 'completed' &&
+          (!pullRequest || headRepoName === element.head_repository.full_name)
         ) {
-          Promise.resolve(cancelRun(octokit, owner, repo, element.id))
+          await cancelRun(octokit, owner, repo, element.id)
         }
       }
     }


[airflow-cancel-workflow-runs] 34/44: Added optional message when cancelling workflow. (#6)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 0ba85cabd56ddb3e40adf6dc76ae4bf7ee88461e
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Wed Oct 7 18:22:15 2020 +0200

    Added optional message when cancelling workflow. (#6)
---
 README.md     | 18 ++++++++++--------
 action.yml    |  4 ++++
 dist/index.js | 13 ++++++++-----
 src/main.ts   | 11 ++++++++---
 4 files changed, 30 insertions(+), 16 deletions(-)

diff --git a/README.md b/README.md
index 7946ea9..a2fcca2 100644
--- a/README.md
+++ b/README.md
@@ -105,14 +105,15 @@ and `schedule` events are no longer needed.
 
 ## Inputs
 
-| Input                  | Required | Default      | Comment                                                                                                                                                                                                          |
-|------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `token`                | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
-| `cancelMode`           | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
-| `sourceRunId`          | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
-| `notifyPRCancel`       | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
-| `notifyPRMessageStart` | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
-| `jobNameRegexps`       | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+| Input                   | Required | Default      | Comment                                                                                                                                                                                                          |
+|-------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `token`                 | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
+| `cancelMode`            | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
+| `sourceRunId`           | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
+| `notifyPRCancel`        | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
+| `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.                                                                                                                           |
+| `notifyPRMessageStart`  | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
+| `jobNameRegexps`        | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
 
 The job cancel modes work as follows:
 
@@ -380,6 +381,7 @@ jobs:
           cancelMode: duplicates
           token: ${{ secrets.GITHUB_TOKEN }}
           notifyPRCancel: true
+          notifyPRCancelMessage: Cancelled because image building failed.
           notifyPRMessageStart: |
             Note! The Docker Images for the build are prepared in a separate workflow,
             that you will not see in the list of checks.
diff --git a/action.yml b/action.yml
index 932b45f..0666499 100644
--- a/action.yml
+++ b/action.yml
@@ -16,6 +16,10 @@ inputs:
       Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why
       they are being cancelled.
     required: false
+  notifyPRCancelMessage:
+    description: |
+      Optional cancel message to use instead of the default one when notifyPRCancel is true.
+    required: false
   notifyPRMessageStart:
     description: |
       Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the
diff --git a/dist/index.js b/dist/index.js
index a7796dd..7f28bf7 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1756,7 +1756,7 @@ function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, ow
             }
             for (const pullRequestNumber of pullRequestToNotify) {
                 const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
-                yield addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, `[The Build Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`);
+                yield addCommentToPullRequest(octokit, owner, repo, pullRequestNumber, `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`);
             }
             core.info('\n######  Finished cancelling runs                  ##########\n');
         }
@@ -1832,7 +1832,7 @@ function getOrigin(octokit, runId, owner, repo) {
         ];
     });
 }
-function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps) {
+function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
@@ -1842,7 +1842,9 @@ function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, own
         let reason = '';
         if (cancelMode === CancelMode.SELF) {
             core.info(`# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`);
-            reason = `The job has been cancelled by another workflow.`;
+            reason = notifyPRCancelMessage
+                ? notifyPRCancelMessage
+                : `The job has been cancelled by another workflow.`;
         }
         else if (cancelMode === CancelMode.FAILED_JOBS) {
             core.info(`# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`);
@@ -1876,6 +1878,7 @@ function run() {
         const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
         const cancelMode = core.getInput('cancelMode') || CancelMode.DUPLICATES;
         const notifyPRCancel = (core.getInput('notifyPRCancel') || 'false').toLowerCase() === 'true';
+        const notifyPRCancelMessage = core.getInput('notifyPRCancelMessage');
         const notifyPRMessageStart = core.getInput('notifyPRMessageStart');
         const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
         const jobNameRegexpsString = core.getInput('jobNameRegexps');
@@ -1923,8 +1926,8 @@ function run() {
                 body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
             });
         }
-        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps);
-        core.setOutput('cancelledRuns', JSON.stringify(cancelledRuns));
+        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps);
+        verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
 run()
diff --git a/src/main.ts b/src/main.ts
index 056ccb8..44ef1bc 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -444,7 +444,7 @@ async function findAndCancelRuns(
         owner,
         repo,
         pullRequestNumber,
-        `[The Build Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`
+        `[The Workflow run](${selfWorkflowRunUrl}) is cancelling this PR. ${reason}`
       )
     }
     core.info(
@@ -568,6 +568,7 @@ async function performCancelJob(
   sourceEventName: string,
   cancelMode: CancelMode,
   notifyPRCancel: boolean,
+  notifyPRCancelMessage: string,
   notifyPRMessageStart: string,
   jobNameRegexps: string[]
 ): Promise<number[]> {
@@ -587,7 +588,9 @@ async function performCancelJob(
     core.info(
       `# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`
     )
-    reason = `The job has been cancelled by another workflow.`
+    reason = notifyPRCancelMessage
+      ? notifyPRCancelMessage
+      : `The job has been cancelled by another workflow.`
   } else if (cancelMode === CancelMode.FAILED_JOBS) {
     core.info(
       `# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`
@@ -643,6 +646,7 @@ async function run(): Promise<void> {
     (core.getInput('cancelMode') as CancelMode) || CancelMode.DUPLICATES
   const notifyPRCancel =
     (core.getInput('notifyPRCancel') || 'false').toLowerCase() === 'true'
+  const notifyPRCancelMessage = core.getInput('notifyPRCancelMessage')
   const notifyPRMessageStart = core.getInput('notifyPRMessageStart')
   const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId
   const jobNameRegexpsString = core.getInput('jobNameRegexps')
@@ -732,11 +736,12 @@ async function run(): Promise<void> {
     sourceEventName,
     cancelMode,
     notifyPRCancel,
+    notifyPRCancelMessage,
     notifyPRMessageStart,
     jobNameRegexps
   )
 
-  core.setOutput('cancelledRuns', JSON.stringify(cancelledRuns))
+  verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))
 }
 
 run()


[airflow-cancel-workflow-runs] 16/44: Add branding info

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 022a420113e1728989d3567ab17607f356c47694
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Wed Feb 19 15:04:37 2020 -0600

    Add branding info
---
 action.yml | 3 +++
 1 file changed, 3 insertions(+)

diff --git a/action.yml b/action.yml
index f9f98bb..d832731 100644
--- a/action.yml
+++ b/action.yml
@@ -8,3 +8,6 @@ inputs:
 runs:
   using: 'node12'
   main: 'dist/index.js'
+branding:
+  icon: 'play'
+  color: 'orange'


[airflow-cancel-workflow-runs] 24/44: Add workflow parameter to inputs list

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 6cc4a33ca01b2c5983cfec0351f4041b719eb6b4
Author: James Netherton <ja...@gmail.com>
AuthorDate: Wed May 20 15:36:21 2020 +0100

    Add workflow parameter to inputs list
---
 README.md   | 2 +-
 action.yml  | 3 +++
 src/main.ts | 2 +-
 3 files changed, 5 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index d25c46f..1448802 100644
--- a/README.md
+++ b/README.md
@@ -5,7 +5,7 @@ This action cancels previous runs for one or more branches/prs associated with a
 
 ## Usage
 
-The easiest and most complete approach to utilize this action, is to create a separate schedule event triggered workflow, which is directed at the workflow you wish to clear duplicate runs. At each cron interrval all branches and all PRs executing for either push or pull_request events will be processed and limited to one run per branch/pr. 
+The easiest and most complete approach to utilize this action, is to create a separate schedule event triggered workflow, which is directed at the workflow you wish to clear duplicate runs. At each cron interval all branches and all PRs executing for either push or pull_request events will be processed and limited to one run per branch/pr.
 
 Additionally this action can be placed as an early step in your workflow (e.g. after checkout), so that it can abort the other previously running jobs immediately, in case most resources are tied up. Unfortunately this approach is a no-op when a pull request uses a fork for a source branch. This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only permissions for security reasons. write permissions are required to be able to cancel a job. Therefore,  [...]
 
diff --git a/action.yml b/action.yml
index 79df71e..e0d9c6d 100644
--- a/action.yml
+++ b/action.yml
@@ -5,6 +5,9 @@ inputs:
   token:
     description: The GITHUB_TOKEN secret of this github workflow
     required: true
+  workflow:
+    description: The filename of the workflow to limit runs on (only applies to schedule events)
+    required: false
 runs:
   using: 'node12'
   main: 'dist/index.js'
diff --git a/src/main.ts b/src/main.ts
index 35ae27f..0fededf 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -45,7 +45,7 @@ async function cancelDuplicates(
 ): Promise<void> {
   const octokit = new github.GitHub(token)
 
-  // Deteermind the workflow to reduce the result set, or reference anothre workflow
+  // Determine the workflow to reduce the result set, or reference another workflow
   let resolvedId = ''
   if (workflowId === undefined) {
     const reply = await octokit.actions.getWorkflowRun({


[airflow-cancel-workflow-runs] 03/44: Fix pagination

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit db63ce1a50987abd5ce462a79f12d93080c69160
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 4 00:40:12 2020 -0600

    Fix pagination
---
 .github/workflows/another.yml | 13 ----------
 __tests__/main.test.ts        | 11 ++++----
 dist/index.js                 | 59 +++++++++++++++++++++++++++++--------------
 src/main.ts                   | 19 ++++++++------
 4 files changed, 56 insertions(+), 46 deletions(-)

diff --git a/.github/workflows/another.yml b/.github/workflows/another.yml
deleted file mode 100644
index e0d2524..0000000
--- a/.github/workflows/another.yml
+++ /dev/null
@@ -1,13 +0,0 @@
-name: "build-test"
-on: # rebuild any PRs and main branch changes
-  pull_request:
-  push:
-
-jobs:
-  build: # make sure build/ci work properly
-    runs-on: ubuntu-latest
-    steps:
-    - uses: actions/checkout@v1
-    - run: |
-       echo Hello
-       sleep 60
diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index bb16f9d..efa2802 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -7,17 +7,16 @@ test('no op', () => {})
 // shows how the runner will run a javascript action with env / stdout protocol
 // test('test runs', () => {
 //   const ip = path.join(__dirname, '..', 'lib', 'main.js')
-//   process.env['INPUT_TOKEN'] = '';
-//   process.env['RUN_ID'] = '33782469';
-//   process.env['GITHUB_REPOSITORY'] = 'protean-project/quarkus-ci';
-//   process.env['GITHUB_REF'] = 'master';
+//   process.env['INPUT_TOKEN'] = ''
+//   process.env['GITHUB_RUN_ID'] = '33782469'
+//   process.env['GITHUB_REPOSITORY'] = 'n1hility/cancel-previous-runs'
+//   process.env['GITHUB_REF'] = 'refs/heads/master'
 //   const options: cp.ExecSyncOptions = {
 //     env: process.env
 //   }
 //   try {
 //     console.log(cp.execSync(`node ${ip}`, options).toString())
 //   } catch (error) {
-//     console.log("Error stdout =  " + error.stdout.toString());
+//     console.log('Error stdout =  ' + error.stdout.toString())
 //   }
-
 // })
diff --git a/dist/index.js b/dist/index.js
index 397e2b1..88b1aee 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1448,6 +1448,13 @@ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, ge
         step((generator = generator.apply(thisArg, _arguments || [])).next());
     });
 };
+var __asyncValues = (this && this.__asyncValues) || function (o) {
+    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
+    var m = o[Symbol.asyncIterator], i;
+    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
+    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
+    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
+};
 var __importStar = (this && this.__importStar) || function (mod) {
     if (mod && mod.__esModule) return mod;
     var result = {};
@@ -1459,6 +1466,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
 function run() {
+    var e_1, _a;
     return __awaiter(this, void 0, void 0, function* () {
         try {
             const token = core.getInput('token');
@@ -1477,30 +1485,43 @@ function run() {
             const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
                 owner,
                 repo,
-                branch,
+                // branch,
                 event: 'push'
             });
-            // eslint-disable-next-line @typescript-eslint/no-explicit-any
-            yield octokit.paginate(listRuns).then((runs) => __awaiter(this, void 0, void 0, function* () {
-                let matched = false;
-                let workflow = '';
-                for (const element of runs) {
-                    core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
-                    if (!matched) {
-                        if (element.id.toString() === selfRunId) {
-                            matched = true;
-                            workflow = element.workflow_url;
+            let matched = false;
+            let workflow = '';
+            let count = 0;
+            try {
+                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                    const item = _c.value;
+                    // There is some sort of bug where the pagination URLs point to a
+                    // different URL with a different data format
+                    const elements = ++count < 2 ? item.data : item.data.workflow_runs;
+                    for (const element of elements) {
+                        core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
+                        if (!matched) {
+                            if (element.id.toString() === selfRunId) {
+                                matched = true;
+                                workflow = element.workflow_url;
+                            }
+                            // Skip everything up to and matching this run
+                            continue;
+                        }
+                        // Only cancel jobs with the same workflow
+                        if (workflow === element.workflow_url &&
+                            element.status.toString() !== 'completed') {
+                            Promise.resolve(cancelRun(octokit, owner, repo, element.id));
                         }
-                        // Skip everything up to and matching this run
-                        continue;
-                    }
-                    // Only cancel jobs with the same workflow
-                    if (workflow === element.workflow_url &&
-                        element.status.toString() !== 'completed') {
-                        yield cancelRun(octokit, owner, repo, element.id);
                     }
                 }
-            }));
+            }
+            catch (e_1_1) { e_1 = { error: e_1_1 }; }
+            finally {
+                try {
+                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+                }
+                finally { if (e_1) throw e_1.error; }
+            }
         }
         catch (error) {
             core.setFailed(error.message);
diff --git a/src/main.ts b/src/main.ts
index e79b04a..44db6fe 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -25,15 +25,18 @@ async function run(): Promise<void> {
     const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
       owner,
       repo,
-      branch,
+      // branch,
       event: 'push'
     })
 
-    // eslint-disable-next-line @typescript-eslint/no-explicit-any
-    await octokit.paginate(listRuns).then(async (runs: any[]) => {
-      let matched = false
-      let workflow = ''
-      for (const element of runs) {
+    let matched = false
+    let workflow = ''
+    let count = 0
+    for await (const item of octokit.paginate.iterator(listRuns)) {
+      // There is some sort of bug where the pagination URLs point to a
+      // different URL with a different data format
+      const elements = ++count < 2 ? item.data : item.data.workflow_runs
+      for (const element of elements) {
         core.info(
           `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
         )
@@ -52,10 +55,10 @@ async function run(): Promise<void> {
           workflow === element.workflow_url &&
           element.status.toString() !== 'completed'
         ) {
-          await cancelRun(octokit, owner, repo, element.id)
+          Promise.resolve(cancelRun(octokit, owner, repo, element.id))
         }
       }
-    })
+    }
   } catch (error) {
     core.setFailed(error.message)
   }


[airflow-cancel-workflow-runs] 09/44: Add initial support for schedule, and refactor accordingly

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 3f0b5bcf961834fa3d9214acd5305e7c181d5d30
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Tue Feb 18 16:24:50 2020 -0600

    Add initial support for schedule, and refactor accordingly
---
 __tests__/main.test.ts |  21 ++++---
 dist/index.js          | 134 +++++++++++++++++++++++++++++---------------
 src/main.ts            | 148 ++++++++++++++++++++++++++++++++++++-------------
 3 files changed, 209 insertions(+), 94 deletions(-)

diff --git a/__tests__/main.test.ts b/__tests__/main.test.ts
index e237067..819d5d4 100644
--- a/__tests__/main.test.ts
+++ b/__tests__/main.test.ts
@@ -7,16 +7,19 @@ test('no op', () => {})
 // shows how the runner will run a javascript action with env / stdout protocol
 // test('test runs', () => {
 //   const ip = path.join(__dirname, '..', 'lib', 'main.js')
-//     process.env['INPUT_TOKEN'] = ''
-//     process.env['GITHUB_RUN_ID'] = '35588693' //'33782469'
-//     process.env['GITHUB_REPOSITORY'] = ''
-//     process.env['GITHUB_HEAD_REF'] = 'refs/heads/n1hility-patch-5'
-//     process.env['GITHUB_EVENT_NAME'] = 'pull_request'
+//   process.env['INPUT_TOKEN'] = ''
+//   //process.env['INPUT_WORKFLOW'] = 'ci-actions.yml'
+//   process.env['GITHUB_RUN_ID'] = '41374869' //'33782469'
+//   process.env['GITHUB_REPOSITORY'] = ''
+//   //process.env['GITHUB_HEAD_REF'] = 'refs/heads/n1hility-patch-5'
+//   process.env['GITHUB_REF'] = 'refs/heads/master'
+//   process.env['GITHUB_EVENT_NAME'] = 'push'
+//   // process.env['GITHUB_EVENT_NAME'] = 'schedule'
 
-// //   process.env['GITHUB_RUN_ID'] = '35599067'
-// //   process.env['GITHUB_REPOSITORY'] = ''
-// //   process.env['GITHUB_REF'] = 'refs/heads/master'
-// //   process.env['GITHUB_EVENT_NAME'] = 'push'
+//   //   process.env['GITHUB_RUN_ID'] = '35599067'
+//   //   process.env['GITHUB_REPOSITORY'] = ''
+//   //   process.env['GITHUB_REF'] = 'refs/heads/master'
+//   //   process.env['GITHUB_EVENT_NAME'] = 'push'
 
 //   const options: cp.ExecSyncOptions = {
 //     env: process.env
diff --git a/dist/index.js b/dist/index.js
index a5661b6..16c7215 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1465,17 +1465,104 @@ var __importStar = (this && this.__importStar) || function (mod) {
 Object.defineProperty(exports, "__esModule", { value: true });
 const github = __importStar(__webpack_require__(469));
 const core = __importStar(__webpack_require__(393));
-function run() {
+function cancelDuplicates(token, selfRunId, owner, repo, workflowId, branch, event) {
     var e_1, _a;
     return __awaiter(this, void 0, void 0, function* () {
+        const octokit = new github.GitHub(token);
+        // Deteermind the workflow to reduce the result set, or reference anothre workflow
+        let resolvedId;
+        if (workflowId === undefined) {
+            const reply = yield octokit.actions.getWorkflowRun({
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                run_id: Number.parseInt(selfRunId)
+            });
+            resolvedId = reply.data.workflow_url.split('/').pop();
+        }
+        else {
+            resolvedId = workflowId;
+        }
+        core.info(`Workflow ID is: ${resolvedId}`);
+        const request = branch === undefined
+            ? {
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                workflow_id: resolvedId
+            }
+            : {
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                workflow_id: resolvedId,
+                branch,
+                event
+            };
+        const listRuns = octokit.actions.listWorkflowRuns.endpoint.merge(request);
+        // If a workflow was provided process everything
+        let matched = workflowId !== undefined;
+        const heads = new Set();
+        try {
+            for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                const item = _c.value;
+                // There is some sort of bug where the pagination URLs point to a
+                // different endpoint URL which trips up the resulting representation
+                // In that case, fallback to the actual REST 'workflow_runs' property
+                const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
+                for (const element of elements) {
+                    core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
+                    if (!matched) {
+                        if (element.id.toString() !== selfRunId) {
+                            // Skip everything up to this run
+                            continue;
+                        }
+                        matched = true;
+                        core.info(`Matched ${selfRunId}`);
+                    }
+                    if ('completed' === element.status.toString()) {
+                        continue;
+                    }
+                    // This is a set of one in the non-schedule case, otherwise everything is a candidate
+                    const head = `${element.head_repository.full_name}/${element.head_branch}`;
+                    if (!heads.has(head)) {
+                        core.info(`First: ${head}`);
+                        heads.add(head);
+                        continue;
+                    }
+                    core.info(`Cancelling: ${head}`);
+                    yield cancelRun(octokit, owner, repo, element.id);
+                }
+            }
+        }
+        catch (e_1_1) { e_1 = { error: e_1_1 }; }
+        finally {
+            try {
+                if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+            }
+            finally { if (e_1) throw e_1.error; }
+        }
+    });
+}
+function run() {
+    return __awaiter(this, void 0, void 0, function* () {
         try {
             const token = core.getInput('token');
+            core.info(token);
             const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
             const repository = getRequiredEnv('GITHUB_REPOSITORY');
             const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
             const [owner, repo] = repository.split('/');
             const branchPrefix = 'refs/heads/';
             const tagPrefix = 'refs/tags/';
+            if ('schedule' === eventName) {
+                const workflowId = core.getInput('workflow');
+                if (!(workflowId.length > 0)) {
+                    throw new Error('Workflow must be specified for schedule event type');
+                }
+                yield cancelDuplicates(token, selfRunId, owner, repo, workflowId);
+                return;
+            }
             if (!['push', 'pull_request'].includes(eventName)) {
                 core.info('Skipping unsupported event');
                 return;
@@ -1492,50 +1579,7 @@ function run() {
             }
             branch = branch.replace(branchPrefix, '');
             core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
-            const octokit = new github.GitHub(token);
-            const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
-                owner,
-                repo,
-                branch,
-                event: pullRequest ? 'pull_request' : 'push'
-            });
-            let matched = false;
-            let workflow = '';
-            let headRepoName = '';
-            try {
-                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
-                    const item = _c.value;
-                    // There is some sort of bug where the pagination URLs point to a
-                    // different endpoint URL which trips up the resulting representation
-                    // In that case, fallback to the actual REST 'workflow_runs' property
-                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
-                    for (const element of elements) {
-                        core.info(`${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`);
-                        if (!matched) {
-                            if (element.id.toString() === selfRunId) {
-                                matched = true;
-                                workflow = element.workflow_url;
-                                headRepoName = pullRequest ? element.head_repository.full_name : '';
-                            }
-                            // Skip everything up to and matching this run
-                            continue;
-                        }
-                        // Only cancel jobs with the same workflow
-                        if (workflow === element.workflow_url &&
-                            element.status.toString() !== 'completed' &&
-                            (!pullRequest || headRepoName === element.head_repository.full_name)) {
-                            yield cancelRun(octokit, owner, repo, element.id);
-                        }
-                    }
-                }
-            }
-            catch (e_1_1) { e_1 = { error: e_1_1 }; }
-            finally {
-                try {
-                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
-                }
-                finally { if (e_1) throw e_1.error; }
-            }
+            cancelDuplicates(token, selfRunId, owner, repo, undefined, branch, eventName);
         }
         catch (error) {
             core.setFailed(error.message);
diff --git a/src/main.ts b/src/main.ts
index c99a264..9ae7083 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -1,10 +1,103 @@
 import * as github from '@actions/github'
 import * as core from '@actions/core'
 
+async function cancelDuplicates(
+  token: string,
+  selfRunId: string,
+  owner: string,
+  repo: string,
+  workflowId?: string,
+  branch?: string,
+  event?: string
+): Promise<void> {
+  const octokit = new github.GitHub(token)
+
+  // Deteermind the workflow to reduce the result set, or reference anothre workflow
+  let resolvedId
+  if (workflowId === undefined) {
+    const reply = await octokit.actions.getWorkflowRun({
+      owner,
+      repo,
+      // eslint-disable-next-line @typescript-eslint/camelcase
+      run_id: Number.parseInt(selfRunId)
+    })
+
+    resolvedId = reply.data.workflow_url.split('/').pop()
+  } else {
+    resolvedId = workflowId
+  }
+
+  core.info(`Workflow ID is: ${resolvedId}`)
+
+  const request =
+    branch === undefined
+      ? {
+          owner,
+          repo,
+          // eslint-disable-next-line @typescript-eslint/camelcase
+          workflow_id: resolvedId
+        }
+      : {
+          owner,
+          repo,
+          // eslint-disable-next-line @typescript-eslint/camelcase
+          workflow_id: resolvedId,
+          branch,
+          event
+        }
+
+  const listRuns = octokit.actions.listWorkflowRuns.endpoint.merge(request)
+
+  // If a workflow was provided process everything
+  let matched = workflowId !== undefined
+  const heads = new Set()
+  for await (const item of octokit.paginate.iterator(listRuns)) {
+    // There is some sort of bug where the pagination URLs point to a
+    // different endpoint URL which trips up the resulting representation
+    // In that case, fallback to the actual REST 'workflow_runs' property
+    const elements =
+      item.data.length === undefined ? item.data.workflow_runs : item.data
+
+    for (const element of elements) {
+      core.info(
+        `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
+      )
+
+      if (!matched) {
+        if (element.id.toString() !== selfRunId) {
+          // Skip everything up to this run
+          continue
+        }
+
+        matched = true
+        core.info(`Matched ${selfRunId}`)
+      }
+
+      if ('completed' === element.status.toString()) {
+        continue
+      }
+
+      // This is a set of one in the non-schedule case, otherwise everything is a candidate
+      const head = `${element.head_repository.full_name}/${element.head_branch}`
+      if (!heads.has(head)) {
+        core.info(`First: ${head}`)
+        heads.add(head)
+        continue
+      }
+
+      core.info(`Cancelling: ${head}`)
+
+      await cancelRun(octokit, owner, repo, element.id)
+    }
+  }
+}
+
 async function run(): Promise<void> {
   try {
     const token = core.getInput('token')
 
+    core.info(token)
+
     const selfRunId = getRequiredEnv('GITHUB_RUN_ID')
     const repository = getRequiredEnv('GITHUB_REPOSITORY')
     const eventName = getRequiredEnv('GITHUB_EVENT_NAME')
@@ -13,6 +106,15 @@ async function run(): Promise<void> {
     const branchPrefix = 'refs/heads/'
     const tagPrefix = 'refs/tags/'
 
+    if ('schedule' === eventName) {
+      const workflowId = core.getInput('workflow')
+      if (!(workflowId.length > 0)) {
+        throw new Error('Workflow must be specified for schedule event type')
+      }
+      await cancelDuplicates(token, selfRunId, owner, repo, workflowId)
+      return
+    }
+
     if (!['push', 'pull_request'].includes(eventName)) {
       core.info('Skipping unsupported event')
       return
@@ -35,49 +137,15 @@ async function run(): Promise<void> {
       `Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`
     )
 
-    const octokit = new github.GitHub(token)
-    const listRuns = octokit.actions.listRepoWorkflowRuns.endpoint.merge({
+    cancelDuplicates(
+      token,
+      selfRunId,
       owner,
       repo,
+      undefined,
       branch,
-      event: pullRequest ? 'pull_request' : 'push'
-    })
-
-    let matched = false
-    let workflow = ''
-    let headRepoName = ''
-    for await (const item of octokit.paginate.iterator(listRuns)) {
-      // There is some sort of bug where the pagination URLs point to a
-      // different endpoint URL which trips up the resulting representation
-      // In that case, fallback to the actual REST 'workflow_runs' property
-      const elements =
-        item.data.length === undefined ? item.data.workflow_runs : item.data
-
-      for (const element of elements) {
-        core.info(
-          `${element.id} : ${element.workflow_url} : ${element.status} : ${element.run_number}`
-        )
-
-        if (!matched) {
-          if (element.id.toString() === selfRunId) {
-            matched = true
-            workflow = element.workflow_url
-            headRepoName = pullRequest ? element.head_repository.full_name : ''
-          }
-          // Skip everything up to and matching this run
-          continue
-        }
-
-        // Only cancel jobs with the same workflow
-        if (
-          workflow === element.workflow_url &&
-          element.status.toString() !== 'completed' &&
-          (!pullRequest || headRepoName === element.head_repository.full_name)
-        ) {
-          await cancelRun(octokit, owner, repo, element.id)
-        }
-      }
-    }
+      eventName
+    )
   } catch (error) {
     core.setFailed(error.message)
   }


[airflow-cancel-workflow-runs] 14/44: Update README.md

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 3f732de7373110ace759279f16752eba1334eaa9
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Wed Feb 19 00:39:53 2020 -0600

    Update README.md
---
 README.md | 29 +++++++++++++++++++++++++----
 1 file changed, 25 insertions(+), 4 deletions(-)

diff --git a/README.md b/README.md
index 76a856f..c357988 100644
--- a/README.md
+++ b/README.md
@@ -1,25 +1,46 @@
 # cancel-previous-runs 
 
-[**BETA**] This action cancels all previous runs on the same branch, effectively limiting the resource consumption of the workflow using this action to one run per branch. 
+[**BETA**] This action cancels previous runs for one or more branches/prs associated with a workflow, effectively limiting the resource consumption of the workflow to one per branch.
 
 <p><a href="https://github.com/actions/typescript-action/actions"><img alt="typescript-action status" src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
 
 ## Usage
 
-This action should be placed as an early step in your workflow (e.g. after chekout), so that it can abort the other running jobs before consuming additional capacity. Additionally, it requires that the running Github Action token (located in the secrets context) be passed as an input parameter so that it can list and cancel workflow runs associated with the workflow's repository.
+The easiest and most complete approach to utilize this action, is to create a separate schedule event triggered workflow, which is directed at the workflow you wish to clear duplicate runs. At each cron interrval all branches and all PRs executing for either push or pull_request events will be processed and limited to one run per branch/pr. 
+
+Additionally this action can be placed as an early step in your workflow (e.g. after chekout), so that it can abort the other previously running jobs immediatily, in case most resources are tied up. Unfortunately this approach is a no-op wheen a pull request uses a fork for a source branch. This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only peermissions for security reasons. write permissions are required to be able to cancel a job. Therefore, [...]
 
 ### Inputs
 
 token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible in the repository, **DO NOT HARDODE A TOKEN ONLY USE A REFERENCE**. 
+workflow - The filename of the workflow to limit runs on (only applies to schedule events) 
+
+
+### Schedule Example
+
+```yaml
+name: Cleanup Duplicate Branches and PRs  
+on:
+  schedule:
+    - cron:  '*/15 * * * *'
+cancel-runs: 
+  runs-on: ubuntu-latest
+    steps:
+      - uses: n1hility/cancel-previous-runs@v2
+        with: 
+          token: ${{ secrets.GITHUB_TOKEN }}
+          workflow: my-heavy-workflow.yml
+```
+
 
-### Example
+### Alternate/Fallback Example
 
 ```yaml
   test: 
     runs-on: ubuntu-latest
     steps:
     - uses: actions/checkout@v1
-    - uses: n1hility/cancel-previous-runs@v1
+    - uses: n1hility/cancel-previous-runs@v2
       with: 
         token: ${{ secrets.GITHUB_TOKEN }}
 ```


[airflow-cancel-workflow-runs] 38/44: Add feature of cancelling future duplicates (enabled by default) (#9)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit c8448eb1e435664b3731ea1ead2efa0d1bb83b5b
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Fri Oct 30 19:32:07 2020 +0100

    Add feature of cancelling future duplicates (enabled by default) (#9)
---
 README.md     | 27 ++++++++++++---------
 action.yml    |  5 ++++
 dist/index.js | 71 +++++++++++++++++++++++++++++++++----------------------
 src/main.ts   | 76 +++++++++++++++++++++++++++++++++++++++--------------------
 4 files changed, 115 insertions(+), 64 deletions(-)

diff --git a/README.md b/README.md
index 90c9a22..0a13160 100644
--- a/README.md
+++ b/README.md
@@ -106,17 +106,18 @@ and `schedule` events are no longer needed.
 
 ## Inputs
 
-| Input                   | Required | Default      | Comment                                                                                                                                                                                                          |
-|-------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
-| `token`                 | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
-| `cancelMode`            | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
-| `sourceRunId`           | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
-| `notifyPRCancel`        | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
-| `notifyPRCancelMessage` | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
-| `notifyPRMessageStart`  | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
-| `jobNameRegexps`        | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
-| `skipEventTypes`        | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
-| `workflowFileName`      | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
+| Input                    | Required | Default      | Comment                                                                                                                                                                                                          |
+|--------------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `token`                  | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
+| `cancelMode`             | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
+| `cancelFutureDuplicates` | no       | true         | In case of duplicate canceling, cancel also future duplicates leaving only the "freshest" running job and not all the future jobs. By default it is set to true.                                                 |
+| `sourceRunId`            | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
+| `notifyPRCancel`         | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
+| `notifyPRCancelMessage`  | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
+| `notifyPRMessageStart`   | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
+| `jobNameRegexps`         | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+| `skipEventTypes`         | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
+| `workflowFileName`       | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
 
 
 The job cancel modes work as follows:
@@ -205,6 +206,7 @@ jobs:
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
+          cancelFutureDuplicates: true
           token: ${{ secrets.GITHUB_TOKEN }}
           sourceRunId: ${{ github.event.workflow_run.id }}
           notifyPRCancel: true
@@ -266,6 +268,7 @@ jobs:
         name: "Cancel duplicate CI runs"
         with:
           cancelMode: duplicates
+          cancelFutureDuplicates: true
           token: ${{ secrets.GITHUB_TOKEN }}
           notifyPRCancel: true
           notifyPRMessageStart: |
@@ -512,6 +515,7 @@ on:
         uses: potiuk/cancel-workflow-runs@v2
         with:
           cancelMode: duplicates
+          cancelFutureDuplicates: true
           token: ${{ secrets.GITHUB_TOKEN }}
           workflowFileName: other_workflow.yml
 ```
@@ -553,6 +557,7 @@ jobs:
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
+          cancelFutureDuplicates: true
           notifyPRCancel: true
 ```
 
diff --git a/action.yml b/action.yml
index 103d4e4..aaec312 100644
--- a/action.yml
+++ b/action.yml
@@ -36,6 +36,11 @@ inputs:
           * `failedJobs`  - cancels all runs that failed in jobs matching one of the regexps
           * `namedJobs`   - cancels runs where names of some jobs match some of regexps
     required: false
+  cancelFutureDuplicates:
+    description: |
+      In case of duplicate canceling, cancel also future duplicates leaving only the "freshest" running
+      job and not all the future jobs. By default it is set to true.
+    required: false
   jobNameRegexps:
     description: |
       Array of job name regexps (JSON-encoded string). Used by `failedJobs` and `namedJobs` cancel modes
diff --git a/dist/index.js b/dist/index.js
index a347a2c..69bd164 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1622,7 +1622,7 @@ function getWorkflowRuns(octokit, statusValues, cancelMode, createListRunQuery)
         return workflowRuns;
     });
 }
-function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNamesRegexps, skipEventTypes) {
+function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNamesRegexps, skipEventTypes) {
     return __awaiter(this, void 0, void 0, function* () {
         if ('completed' === runItem.status.toString()) {
             core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
@@ -1651,17 +1651,17 @@ function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode,
         else if (cancelMode === CancelMode.NAMED_JOBS) {
             // Cancel all jobs that have failed jobs (no matter when started)
             if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, false)) {
-                core.info(`\nSome jobs have matching names in ${runItem.id} . Cancelling it.\n`);
+                core.info(`\nSome jobs have matching names in ${runItem.id} . Returning it.\n`);
                 return true;
             }
             else {
-                core.info(`\nNone of the jobs match name in ${runItem.id}. Not cancelling it.\n`);
+                core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`);
                 return false;
             }
         }
         else if (cancelMode === CancelMode.SELF) {
             if (runItem.id === sourceRunId) {
-                core.info(`\nCancelling the "source" run: ${runItem.id}.\n`);
+                core.info(`\nReturning the "source" run: ${runItem.id}.\n`);
                 return true;
             }
             else {
@@ -1675,16 +1675,20 @@ function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode,
                     `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
                 return false;
             }
-            if (runItem.id === sourceRunId) {
-                core.info(`\nThis is my own run ${runItem.id}. I have self-preservation mechanism. Not cancelling myself!\n`);
-                return false;
-            }
-            else if (runItem.id > sourceRunId) {
-                core.info(`\nThe run ${runItem.id} is started later than mt own run ${sourceRunId}. Not cancelling it\n`);
-                return false;
+            if (cancelFutureDuplicates) {
+                core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
+                return true;
             }
             else {
-                core.info(`\nCancelling duplicate of my own run: ${runItem.id}.\n`);
+                if (runItem.id === sourceRunId) {
+                    core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`);
+                    return false;
+                }
+                else if (runItem.id > sourceRunId) {
+                    core.info(`\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`);
+                    return false;
+                }
+                core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`);
                 return true;
             }
         }
@@ -1710,7 +1714,7 @@ function cancelRun(octokit, owner, repo, runId) {
         }
     });
 }
-function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
         const workflowRuns = yield getWorkflowRuns(octokit, statusValues, cancelMode, function (status) {
@@ -1735,29 +1739,39 @@ function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, ow
                 throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
             }
         });
-        const idsToCancel = [];
+        const workflowsToCancel = [];
         const pullRequestToNotify = [];
         for (const [key, runItem] of workflowRuns) {
-            core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`);
-            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNameRegexps, skipEventTypes)) {
+            core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
+                ` Created at ${runItem.created_at}\n`);
+            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes)) {
                 if (notifyPRCancel && runItem.event === 'pull_request') {
                     const pullRequest = yield findPullRequest(octokit, owner, repo, runItem.head_repository.owner.login, runItem.head_branch, runItem.head_sha);
                     if (pullRequest) {
                         pullRequestToNotify.push(pullRequest.number);
                     }
                 }
-                idsToCancel.push(runItem.id);
+                workflowsToCancel.push([runItem.id, runItem.created_at]);
             }
         }
-        // Sort from smallest number - this way we always kill current one at the end (if we kill it at all)
-        const sortedIdsToCancel = idsToCancel.sort((id1, id2) => id1 - id2);
-        if (sortedIdsToCancel.length > 0) {
+        // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
+        const sortedRunTuplesToCancel = workflowsToCancel.sort((runTuple1, runTuple2) => runTuple2[1].localeCompare(runTuple1[1]));
+        if (sortedRunTuplesToCancel.length > 0) {
+            if (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) {
+                core.info(`\nSkipping the first run (${sortedRunTuplesToCancel[0]}) of all the matching ` +
+                    `duplicates - this one we are going to leave in peace!\n`);
+                sortedRunTuplesToCancel.shift();
+            }
+            if (sortedRunTuplesToCancel.length === 0) {
+                core.info(`\nNo duplicates to cancel!\n`);
+                return sortedRunTuplesToCancel.map(runTuple => runTuple[0]);
+            }
             core.info('\n######  Cancelling runs starting from the oldest  ##########\n' +
-                `\n     Runs to cancel: ${sortedIdsToCancel.length}\n` +
+                `\n     Runs to cancel: ${sortedRunTuplesToCancel.length}\n` +
                 `\n     PRs to notify: ${pullRequestToNotify.length}\n`);
-            for (const runId of sortedIdsToCancel) {
-                core.info(`\nCancelling run: ${runId}.\n`);
-                yield cancelRun(octokit, owner, repo, runId);
+            for (const runTuple of sortedRunTuplesToCancel) {
+                core.info(`\nCancelling run: ${runTuple}.\n`);
+                yield cancelRun(octokit, owner, repo, runTuple[0]);
             }
             for (const pullRequestNumber of pullRequestToNotify) {
                 const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`;
@@ -1768,7 +1782,7 @@ function findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, ow
         else {
             core.info('\n######  There are no runs to cancel!              ##########\n');
         }
-        return sortedIdsToCancel;
+        return sortedRunTuplesToCancel.map(runTuple => runTuple[0]);
     });
 }
 function getRequiredEnv(key) {
@@ -1837,7 +1851,7 @@ function getOrigin(octokit, runId, owner, repo) {
         ];
     });
 }
-function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes) {
+function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
         core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
@@ -1867,7 +1881,7 @@ function performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, own
             throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+        return yield findAndCancelRuns(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
     });
 }
 function verboseOutput(name, value) {
@@ -1887,6 +1901,7 @@ function run() {
         const notifyPRMessageStart = core.getInput('notifyPRMessageStart');
         const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
         const jobNameRegexpsString = core.getInput('jobNameRegexps');
+        const cancelFutureDuplicates = (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true';
         const jobNameRegexps = jobNameRegexpsString
             ? JSON.parse(jobNameRegexpsString)
             : [];
@@ -1944,7 +1959,7 @@ function run() {
                 body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
             });
         }
-        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes);
+        const cancelledRuns = yield performCancelJob(octokit, selfRunId, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index 9f60f3d..71da8d9 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -203,6 +203,7 @@ async function shouldBeCancelled(
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
   headRepo: string,
   cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
   sourceRunId: number,
   jobNamesRegexps: string[],
   skipEventTypes: string[]
@@ -259,18 +260,18 @@ async function shouldBeCancelled(
       )
     ) {
       core.info(
-        `\nSome jobs have matching names in ${runItem.id} . Cancelling it.\n`
+        `\nSome jobs have matching names in ${runItem.id} . Returning it.\n`
       )
       return true
     } else {
       core.info(
-        `\nNone of the jobs match name in ${runItem.id}. Not cancelling it.\n`
+        `\nNone of the jobs match name in ${runItem.id}. Returning it.\n`
       )
       return false
     }
   } else if (cancelMode === CancelMode.SELF) {
     if (runItem.id === sourceRunId) {
-      core.info(`\nCancelling the "source" run: ${runItem.id}.\n`)
+      core.info(`\nReturning the "source" run: ${runItem.id}.\n`)
       return true
     } else {
       return false
@@ -284,18 +285,22 @@ async function shouldBeCancelled(
       )
       return false
     }
-    if (runItem.id === sourceRunId) {
+    if (cancelFutureDuplicates) {
       core.info(
-        `\nThis is my own run ${runItem.id}. I have self-preservation mechanism. Not cancelling myself!\n`
+        `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
       )
-      return false
-    } else if (runItem.id > sourceRunId) {
-      core.info(
-        `\nThe run ${runItem.id} is started later than mt own run ${sourceRunId}. Not cancelling it\n`
-      )
-      return false
+      return true
     } else {
-      core.info(`\nCancelling duplicate of my own run: ${runItem.id}.\n`)
+      if (runItem.id === sourceRunId) {
+        core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`)
+        return false
+      } else if (runItem.id > sourceRunId) {
+        core.info(
+          `\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`
+        )
+        return false
+      }
+      core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`)
       return true
     }
   } else {
@@ -338,6 +343,7 @@ async function findAndCancelRuns(
   headBranch: string,
   sourceEventName: string,
   cancelMode: CancelMode,
+  cancelFutureDuplicates: boolean,
   notifyPRCancel: boolean,
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
@@ -400,11 +406,12 @@ async function findAndCancelRuns(
       }
     }
   )
-  const idsToCancel: number[] = []
+  const workflowsToCancel: [number, string][] = []
   const pullRequestToNotify: number[] = []
   for (const [key, runItem] of workflowRuns) {
     core.info(
-      `\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`
+      `\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
+        ` Created at ${runItem.created_at}\n`
     )
     if (
       await shouldBeCancelled(
@@ -414,6 +421,7 @@ async function findAndCancelRuns(
         runItem,
         headRepo,
         cancelMode,
+        cancelFutureDuplicates,
         sourceRunId,
         jobNameRegexps,
         skipEventTypes
@@ -432,20 +440,33 @@ async function findAndCancelRuns(
           pullRequestToNotify.push(pullRequest.number)
         }
       }
-      idsToCancel.push(runItem.id)
+      workflowsToCancel.push([runItem.id, runItem.created_at])
     }
   }
-  // Sort from smallest number - this way we always kill current one at the end (if we kill it at all)
-  const sortedIdsToCancel = idsToCancel.sort((id1, id2) => id1 - id2)
-  if (sortedIdsToCancel.length > 0) {
+  // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
+  const sortedRunTuplesToCancel = workflowsToCancel.sort(
+    (runTuple1, runTuple2) => runTuple2[1].localeCompare(runTuple1[1])
+  )
+  if (sortedRunTuplesToCancel.length > 0) {
+    if (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) {
+      core.info(
+        `\nSkipping the first run (${sortedRunTuplesToCancel[0]}) of all the matching ` +
+          `duplicates - this one we are going to leave in peace!\n`
+      )
+      sortedRunTuplesToCancel.shift()
+    }
+    if (sortedRunTuplesToCancel.length === 0) {
+      core.info(`\nNo duplicates to cancel!\n`)
+      return sortedRunTuplesToCancel.map(runTuple => runTuple[0])
+    }
     core.info(
       '\n######  Cancelling runs starting from the oldest  ##########\n' +
-        `\n     Runs to cancel: ${sortedIdsToCancel.length}\n` +
+        `\n     Runs to cancel: ${sortedRunTuplesToCancel.length}\n` +
         `\n     PRs to notify: ${pullRequestToNotify.length}\n`
     )
-    for (const runId of sortedIdsToCancel) {
-      core.info(`\nCancelling run: ${runId}.\n`)
-      await cancelRun(octokit, owner, repo, runId)
+    for (const runTuple of sortedRunTuplesToCancel) {
+      core.info(`\nCancelling run: ${runTuple}.\n`)
+      await cancelRun(octokit, owner, repo, runTuple[0])
     }
     for (const pullRequestNumber of pullRequestToNotify) {
       const selfWorkflowRunUrl = `https://github.com/${owner}/${repo}/actions/runs/${selfRunId}`
@@ -465,7 +486,7 @@ async function findAndCancelRuns(
       '\n######  There are no runs to cancel!              ##########\n'
     )
   }
-  return sortedIdsToCancel
+  return sortedRunTuplesToCancel.map(runTuple => runTuple[0])
 }
 
 function getRequiredEnv(key: string): string {
@@ -581,7 +602,8 @@ async function performCancelJob(
   notifyPRCancelMessage: string,
   notifyPRMessageStart: string,
   jobNameRegexps: string[],
-  skipEventTypes: string[]
+  skipEventTypes: string[],
+  cancelFutureDuplicates: boolean
 ): Promise<number[]> {
   core.info(
     '\n###################################################################################\n'
@@ -635,6 +657,7 @@ async function performCancelJob(
     headBranch,
     sourceEventName,
     cancelMode,
+    cancelFutureDuplicates,
     notifyPRCancel,
     notifyPRMessageStart,
     jobNameRegexps,
@@ -662,6 +685,8 @@ async function run(): Promise<void> {
   const notifyPRMessageStart = core.getInput('notifyPRMessageStart')
   const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId
   const jobNameRegexpsString = core.getInput('jobNameRegexps')
+  const cancelFutureDuplicates =
+    (core.getInput('cancelFutureDuplicates') || 'true').toLowerCase() === 'true'
   const jobNameRegexps = jobNameRegexpsString
     ? JSON.parse(jobNameRegexpsString)
     : []
@@ -761,7 +786,8 @@ async function run(): Promise<void> {
     notifyPRCancelMessage,
     notifyPRMessageStart,
     jobNameRegexps,
-    skipEventTypes
+    skipEventTypes,
+    cancelFutureDuplicates
   )
 
   verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns))


[airflow-cancel-workflow-runs] 43/44: Fixed too-agressive (cross-branches) cancelling

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit f4a33154219b13dbb1e171695d6f03810f3a7b47
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 19:01:45 2020 +0100

    Fixed too-agressive (cross-branches) cancelling
    
    There was an error introduced in v4_3 that cancelled cross-PR builds
    
    This change fixes it
---
 dist/index.js | 31 ++++++++++++++++++++-----------
 src/main.ts   | 46 +++++++++++++++++++++++++++-------------------
 2 files changed, 47 insertions(+), 30 deletions(-)

diff --git a/dist/index.js b/dist/index.js
index a46b432..e6422c4 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1491,13 +1491,22 @@ var CancelMode;
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
  * @param triggeringRunInfo the object identifying the triggering workflow
- * @returns the unique string id for the source group
+ * @returns the unique string id for the group
  */
-function getSourceGroupId(triggeringRunInfo) {
+function getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo) {
     return (`:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
         `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`);
 }
 /**
+ * Converts the source of a run object into a string that can be used as map key in maps where we keep
+ * arrays of runs per group
+ * @param runItem Item the run item to retrieve the group from
+ * @returns the unique string id for the group
+ */
+function getCommonGroupIdFromRunItem(runItem) {
+    return `:${retrieveWorkflowIdFromUrl(runItem.workflow_url)}:${runItem.head_repository.full_name}:${runItem.head_branch}:${runItem.event}`;
+}
+/**
  * Creates query parameters selecting all runs that share the same source group as we have. This can
  * be used to select duplicates of my own run.
  *
@@ -1594,6 +1603,7 @@ function matchInArray(stringToMatch, regexps) {
  * @param mapOfWorkflowRunCandidates map of workflow runs to add the run item to
  */
 function addWorkflowRunToMap(key, runItem, mapOfWorkflowRunCandidates) {
+    core.info(`\nAdding the run: ${runItem.id} to candidates with ${key} key.\n`);
     let arrayOfRuns = mapOfWorkflowRunCandidates.get(key);
     if (arrayOfRuns === undefined) {
         arrayOfRuns = [];
@@ -1632,12 +1642,12 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
                     const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps);
                     if (jobMatched) {
                         allMatches.push(...jobMatches);
-                        matched = true;
                         if (checkIfFailed) {
                             // Only fail the build if one of the matching jobs fail
                             if (job.conclusion === 'failure') {
                                 core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
                                     ` It will be added to the candidates.`);
+                                matched = true;
                             }
                             else {
                                 core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -1648,6 +1658,7 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
                             // Fail the build if any of the job names match
                             core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
                                 `It will be added to the candidates.`);
+                            matched = true;
                         }
                     }
                 }
@@ -1748,7 +1759,7 @@ function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, t
     }
     if (cancelFutureDuplicates) {
         core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
-        addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+        addWorkflowRunToMap(getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
     }
     else {
         if (runItem.id === triggeringRunInfo.runId) {
@@ -1769,8 +1780,7 @@ function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, t
  */
 function checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
     if (runItem.id === triggeringRunInfo.runId) {
-        core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`);
-        addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+        addWorkflowRunToMap('selfRun', runItem, mapOfWorkflowRunCandidates);
     }
 }
 /**
@@ -1780,8 +1790,7 @@ function checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkfl
  * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  */
 function checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
-    core.info(`\nAdding the run: ${runItem.id} to candidates.\n`);
-    addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+    addWorkflowRunToMap(getCommonGroupIdFromRunItem(runItem), runItem, mapOfWorkflowRunCandidates);
 }
 /**
  * Should the run is candidate for cancelling in naming job cancelling mode?
@@ -1797,8 +1806,8 @@ function checkCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesR
         // Cancel all jobs that have failed jobs (no matter when started)
         const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false);
         if (matched) {
-            core.info(`\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`);
-            addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+            core.info(`\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it as candidate.\n`);
+            addWorkflowRunToMap('allMatchingNamedJobs', runItem, mapOfWorkflowRunCandidates);
         }
         else {
             core.info(`\nNone of the jobs match name in ${runItem.id}.\n`);
@@ -1820,7 +1829,7 @@ function checkCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNames
         const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true);
         if (matched) {
             core.info(`\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`);
-            addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+            addWorkflowRunToMap('failedJobs', runItem, mapOfWorkflowRunCandidates);
         }
         else {
             core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`);
diff --git a/src/main.ts b/src/main.ts
index 7ee681b..cd99a35 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -53,9 +53,11 @@ interface TriggeringRunInfo {
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
  * @param triggeringRunInfo the object identifying the triggering workflow
- * @returns the unique string id for the source group
+ * @returns the unique string id for the group
  */
-function getSourceGroupId(triggeringRunInfo: TriggeringRunInfo): string {
+function getCommonGroupIdFromTriggeringRunInfo(
+  triggeringRunInfo: TriggeringRunInfo
+): string {
   return (
     `:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
     `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`
@@ -63,6 +65,20 @@ function getSourceGroupId(triggeringRunInfo: TriggeringRunInfo): string {
 }
 
 /**
+ * Converts the source of a run object into a string that can be used as map key in maps where we keep
+ * arrays of runs per group
+ * @param runItem Item the run item to retrieve the group from
+ * @returns the unique string id for the group
+ */
+function getCommonGroupIdFromRunItem(
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem
+): string {
+  return `:${retrieveWorkflowIdFromUrl(runItem.workflow_url)}:${
+    runItem.head_repository.full_name
+  }:${runItem.head_branch}:${runItem.event}`
+}
+
+/**
  * Creates query parameters selecting all runs that share the same source group as we have. This can
  * be used to select duplicates of my own run.
  *
@@ -190,6 +206,7 @@ function addWorkflowRunToMap(
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
   >
 ): void {
+  core.info(`\nAdding the run: ${runItem.id} to candidates with ${key} key.\n`)
   let arrayOfRuns = mapOfWorkflowRunCandidates.get(key)
   if (arrayOfRuns === undefined) {
     arrayOfRuns = []
@@ -233,7 +250,6 @@ async function jobsMatchingNames(
       const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps)
       if (jobMatched) {
         allMatches.push(...jobMatches)
-        matched = true
         if (checkIfFailed) {
           // Only fail the build if one of the matching jobs fail
           if (job.conclusion === 'failure') {
@@ -241,6 +257,7 @@ async function jobsMatchingNames(
               `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
                 ` It will be added to the candidates.`
             )
+            matched = true
           } else {
             core.info(
               `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -253,6 +270,7 @@ async function jobsMatchingNames(
             `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
               `It will be added to the candidates.`
           )
+          matched = true
         }
       }
     }
@@ -362,7 +380,7 @@ function checkCandidateForCancellingDuplicate(
       `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
     )
     addWorkflowRunToMap(
-      getSourceGroupId(triggeringRunInfo),
+      getCommonGroupIdFromTriggeringRunInfo(triggeringRunInfo),
       runItem,
       mapOfWorkflowRunCandidates
     )
@@ -394,12 +412,7 @@ function checkCandidateForCancellingSelf(
   >
 ): void {
   if (runItem.id === triggeringRunInfo.runId) {
-    core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`)
-    addWorkflowRunToMap(
-      getSourceGroupId(triggeringRunInfo),
-      runItem,
-      mapOfWorkflowRunCandidates
-    )
+    addWorkflowRunToMap('selfRun', runItem, mapOfWorkflowRunCandidates)
   }
 }
 
@@ -417,9 +430,8 @@ function checkCandidateForAllDuplicates(
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
   >
 ): void {
-  core.info(`\nAdding the run: ${runItem.id} to candidates.\n`)
   addWorkflowRunToMap(
-    getSourceGroupId(triggeringRunInfo),
+    getCommonGroupIdFromRunItem(runItem),
     runItem,
     mapOfWorkflowRunCandidates
   )
@@ -453,10 +465,10 @@ async function checkCandidateForCancellingNamedJobs(
   )
   if (matched) {
     core.info(
-      `\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`
+      `\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it as candidate.\n`
     )
     addWorkflowRunToMap(
-      getSourceGroupId(triggeringRunInfo),
+      'allMatchingNamedJobs',
       runItem,
       mapOfWorkflowRunCandidates
     )
@@ -495,11 +507,7 @@ async function checkCandidateForCancellingFailedJobs(
     core.info(
       `\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`
     )
-    addWorkflowRunToMap(
-      getSourceGroupId(triggeringRunInfo),
-      runItem,
-      mapOfWorkflowRunCandidates
-    )
+    addWorkflowRunToMap('failedJobs', runItem, mapOfWorkflowRunCandidates)
   } else {
     core.info(
       `\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`


[airflow-cancel-workflow-runs] 28/44: Move to workflow_run triggered cancelling

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 2210b4b3ae7b65d20c85f1d46048788fac31e7b9
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Fri Aug 7 17:51:56 2020 +0200

    Move to workflow_run triggered cancelling
    
    After Github Actions introduced `workflow_run` triggere it is
    no longer needed to cancel pull request runs from forks via
    scheduled runs.
    
    Several `cancelModes` were introduced in order to handle all
    cases where canceling runs is desired.
    
    Documentation has been improved showing all cases
    and examples how you should implement your own workflow
    in those cases.
---
 .eslintignore              |    2 +-
 .eslintrc.json             |    2 +-
 .github/workflows/test.yml |   49 +-
 .pre-commit-config.yaml    |   47 +
 .prettierignore            |    2 +-
 .prettierrc.json           |    2 +-
 LICENSE                    |    2 +-
 README.md                  |  638 +++-
 action.yml                 |   26 +-
 dist/index.js              |  729 ++--
 jest.config.js             |    2 +-
 package-lock.json          | 9055 ++++++++++++++++++++++++++++++++------------
 package.json               |    6 +-
 src/main.ts                |  702 ++--
 yamllint-config.yml        |   22 +
 15 files changed, 8211 insertions(+), 3075 deletions(-)

diff --git a/.eslintignore b/.eslintignore
index 2186947..6de9a76 100644
--- a/.eslintignore
+++ b/.eslintignore
@@ -1,3 +1,3 @@
 dist/
 lib/
-node_modules/
\ No newline at end of file
+node_modules/
diff --git a/.eslintrc.json b/.eslintrc.json
index 09d81aa..7d0b5de 100644
--- a/.eslintrc.json
+++ b/.eslintrc.json
@@ -55,4 +55,4 @@
       "es6": true,
       "jest/globals": true
     }
-  }
\ No newline at end of file
+  }
diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml
index 61c2baf..868c25f 100644
--- a/.github/workflows/test.yml
+++ b/.github/workflows/test.yml
@@ -1,27 +1,36 @@
-name: "build-test"
+name: "Test the build"
 on: # rebuild any PRs and main branch changes
   pull_request:
   push:
 
 jobs:
-  build: # make sure build/ci work properly
+  pre-commit: # make sure pre-commits work properly
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v1
-    - run: |
-        npm install
-        npm run all
-  test: # make sure the action works on a clean machine without building
-    name: Test on ${{ matrix.os }}
-
-    strategy:
-      matrix:
-        os: [ubuntu-latest, windows-latest, macOS-latest]
-
-    runs-on: ${{ matrix.os }}
-    
-    steps:
-    - uses: actions/checkout@v1
-    - uses: ./
-      with: 
-        token: ${{ secrets.GITHUB_TOKEN }}
+      - uses: actions/checkout@v2
+      - uses: actions/setup-python@v2
+        with:
+          python-version: 3.6
+      - name: Cache npm env
+        uses: actions/cache@v2
+        env:
+          cache-name: cache-npm-v1
+        with:
+          path: node_modules
+          key: ${{ env.cache-name }}-${{ github.job }}-${{ hashFiles('package.json','package-lock.json') }}
+      - name: "Install dependencies for npm"
+        run: |
+          npm ci
+      - name: Cache pre-commit env
+        uses: actions/cache@v2
+        env:
+          cache-name: cache-pre-commit-v1
+        with:
+          path: ~/.cache/pre-commit
+          key: ${{ env.cache-name }}-${{ github.job }}-${{ hashFiles('.pre-commit-config.yaml') }}
+      - name: "Install pre-commit"
+        run: |
+          pip install pre-commit
+      - name: "Run pre-commit"
+        run: |
+          pre-commit run --all-files --show-diff-on-failure --color always
diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml
new file mode 100644
index 0000000..ec972f3
--- /dev/null
+++ b/.pre-commit-config.yaml
@@ -0,0 +1,47 @@
+---
+default_stages: [commit, push]
+default_language_version:
+  # force all unspecified python hooks to run python3
+  python: python3
+minimum_pre_commit_version: "1.20.0"
+repos:
+  - repo: https://github.com/Lucas-C/pre-commit-hooks
+    rev: v1.1.7
+    hooks:
+      - id: forbid-tabs
+        exclude: ^dist/index.js$
+  - repo: https://github.com/thlorenz/doctoc.git
+    rev: v1.4.0
+    hooks:
+      - id: doctoc
+        name: Add TOC for md files
+        files: ^README\.md$|^CONTRIBUTING\.md$|^UPDATING.md$|^dev/README\.md$|^dev/BACKPORT_PACKAGES.md$
+  - repo: meta
+    hooks:
+      - id: check-hooks-apply
+  - repo: https://github.com/adrienverge/yamllint
+    rev: v1.23.0
+    hooks:
+      - id: yamllint
+        name: Check yaml files with yamllint
+        entry: yamllint -c yamllint-config.yml
+        types: [yaml]
+        exclude: ^.*init_git_sync\.template\.yaml$|^.*airflow\.template\.yaml$|^chart/templates/.*\.yaml$
+  - repo: local
+    hooks:
+      - id: build
+        name: Build package for distribution
+        language: system
+        entry: bash -c "npm run release"
+        files: .*\.ts$
+        require_serial: true
+        pass_filenames: false
+  - repo: https://github.com/pre-commit/pre-commit-hooks
+    rev: v3.1.0
+    hooks:
+      - id: check-merge-conflict
+      - id: detect-private-key
+      - id: end-of-file-fixer
+        exclude: ^dist/.*
+      - id: trailing-whitespace
+        exclude: ^dist/.*
diff --git a/.prettierignore b/.prettierignore
index 2186947..6de9a76 100644
--- a/.prettierignore
+++ b/.prettierignore
@@ -1,3 +1,3 @@
 dist/
 lib/
-node_modules/
\ No newline at end of file
+node_modules/
diff --git a/.prettierrc.json b/.prettierrc.json
index bbf4311..9391cfc 100644
--- a/.prettierrc.json
+++ b/.prettierrc.json
@@ -8,4 +8,4 @@
     "bracketSpacing": false,
     "arrowParens": "avoid",
     "parser": "typescript"
-  }
\ No newline at end of file
+  }
diff --git a/LICENSE b/LICENSE
index a426ef2..a67dca8 100644
--- a/LICENSE
+++ b/LICENSE
@@ -19,4 +19,4 @@ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
 AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
 LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
 OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
-THE SOFTWARE.
\ No newline at end of file
+THE SOFTWARE.
diff --git a/README.md b/README.md
index 16c7a62..f35d466 100644
--- a/README.md
+++ b/README.md
@@ -1,106 +1,586 @@
-# cancel-workflow-runs
-This action cancels runs for one or more branches/prs associated with a workflow,
-effectively limiting the resource consumption of the workflow to one per branch.
-
-It also cancels workflows from the latest workflow run if specified jobs failed.
-That allows to further limit the resource usage of running workflows, without
-impacting the elapsed time of successful workflow runs. Typical behaviour of
-the Github Actions Workflow is that the success/failure propagation between the jobs
-happens through job dependency graph (needs: in the GA yaml). However, there are cases
-where you want to start some jobs without waiting for other jobs to succeed, yet if
-the other jobs fail, you want to cancel the whole workflow. It's similar to
-fail-fast behaviour of the matrix builds.
-
-Since cancelling workflow does not work from "fork" pull requests for security reasons,
-the capability of canceling the workflows should be built in the scheduled task.
-
-<p><a href="https://github.com/actions/typescript-action/actions">
-<img alt="typescript-action status"
-    src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
-
-I based the implementation of this action on the
-[n1hility action](https://github.com/n1hility/cancel-previous-runs) to cancel the previous runs only.
-
-## Usage
-
-The easiest and most complete approach to utilize this action, is to create a separate schedule event
-triggered workflow, which is directed at the workflow you wish to clear duplicate runs.
-At each cron interval all branches and all PRs executing for either push or pull_request events
-will be processed and limited to one run per branch/pr.
-
-Additionally, this action can be placed as an early step in your workflow (e.g. after the checkout), so
-that it can abort the other previously running jobs immediately, in case the workflows tie up most resources.
-Unfortunately this approach is a no-op when a pull request uses a fork for a source branch.
-This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only
-permissions for security reasons. You need write permissions to be able to cancel a job.
-Therefore, it's a good idea to only rely on this approach as a fallback in-addition to the previously
-described scheduling model.
-
-### Inputs
-
-* token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible
-  in the repository, **DO NOT HARDCODE A TOKEN ONLY USE A REFERENCE**.
-* workflow - The filename of the workflow to limit runs on (only applies to schedule events)
-* failFastJobNames - optional array of job name regexps. If a job name that matches any of the regexp fails
-  in the most recent run, this causes a fail-fast of the run. This can be used if you want to run jobs
-  in parallel but kill them as soon as some of those jobs fail - effectively turning them into "fail-fast"
-  type of jobs. Note these are job names after interpolation of workflow variables - so you have to make sure that
-  you use the name as displayed in the status of the workflow or use regexp to
-  match the names.
-
-### Schedule Example
+<p><a href="https://github.com/potiuk/cancel-workflow-runs/actions">
+<img alt="cancel-workflow-runs status"
+    src="https://github.com/potiuk/cancel-workflow-runs/workflows/Test%20the%20build/badge.svg"></a>
+
+# Cancel Workflow Runs action
+
+
+<!-- START doctoc generated TOC please keep comment here to allow auto update -->
+<!-- DON'T EDIT THIS SECTION, INSTEAD RE-RUN doctoc TO UPDATE -->
+**Table of Contents**  *generated with [DocToc](https://github.com/thlorenz/doctoc)*
+
+- [Context and motivation](#context-and-motivation)
+- [Usage](#usage)
+- [Inputs and outputs](#inputs-and-outputs)
+  - [Inputs](#inputs)
+  - [Outputs](#outputs)
+- [Examples](#examples)
+  - [Repositories that do not use Pull Requests from forks](#repositories-that-do-not-use-pull-requests-from-forks)
+    - [Cancel duplicate runs for "self" workflow](#cancel-duplicate-runs-for-self-workflow)
+    - [Cancel "self" workflow run](#cancel-self-workflow-run)
+    - [Fail-fast workflow runs with failed jobs](#fail-fast-workflow-runs-with-failed-jobs)
+    - [Cancel all runs with named jobs](#cancel-all-runs-with-named-jobs)
+  - [Repositories that use Pull Requests from forks](#repositories-that-use-pull-requests-from-forks)
+    - [Cancel duplicate runs for the source workflow](#cancel-duplicate-runs-for-the-source-workflow)
+    - [Cancel duplicate jobs for triggered workflow](#cancel-duplicate-jobs-for-triggered-workflow)
+    - [Cancel the "self" source workflow run](#cancel-the-self-source-workflow-run)
+    - [Cancel the "self" triggered workflow run](#cancel-the-self-triggered-workflow-run)
+    - [Fail-fast source workflow runs with failed jobs](#fail-fast-source-workflow-runs-with-failed-jobs)
+    - [Fail-fast source workflow runs with failed jobs and corresponding triggered runs](#fail-fast-source-workflow-runs-with-failed-jobs-and-corresponding-triggered-runs)
+    - [Fail-fast for triggered workflow runs with failed jobs](#fail-fast-for-triggered-workflow-runs-with-failed-jobs)
+  - [Development environment](#development-environment)
+  - [License](#license)
+
+<!-- END doctoc generated TOC please keep comment here to allow auto update -->
+
+# Context and motivation
+
+Cancel Workflow Runs is an action that utilizes `workflow_run` triggers in order to perform various
+run cancel operations. The idea is to save number of jobs and free them for other queued runs. It is
+particularly useful in case your projects development flow where contributors submit pull requests
+from forks. Using `workflow_run` trigger enables safe canceling of runs triggered by such pull requests.
+
+In case your CI takes a lot of time and uses a lot of jobs, the action might help your project
+to reduce job usage and decrease waiting time as it detects and cancels runs that are still executed,
+but we know already they are superseded by newer runs.
+
+The main purpose of this action is canceling duplicated runs for the same branch as the current run,
+effectively limiting the resource consumption of the workflow to one run per branch. In short, the action
+is useful if you want to limit jobs usage on GitHub Actions in case of the usage pattern
+when fixups/rebases are pushed in quick succession to the same branch (fast iterations on a Pull Request).
+This is achieved by `duplicates` cancel mode. The `duplicates` mode only cancels "past" runs - it does
+not take into account runs that were started after the "current" run.
+
+Another use case is to cancel the `pull_request` corresponding to the `workflow_run` triggered run.
+This can happen when the triggered `workflow_run` finds that it makes no sense to proceed with
+the source run. This is achieved by `self` cancel mode.
+
+There are also two supplementary cancel modes for the action. Those supplementary use cases allow for further
+optimisations - failing fast in case we detect that important job failed and canceling duplicates of the
+`workflow_run` triggered events in case they execute some heavy jobs. This is achieved by `failedJobs` and
+`namedJobs` cancel modes.
+
+Note that `namedjobs` cancel mode is solely for the purpose of bypassing current limitations
+of GitHub Actions. Currently, there is no way to retrieve connection between triggering and triggered
+workflow in case of `workflow_run`, as well as retrieving repository and branch of the triggering
+workflow. The action uses workaround - it requires designing workflows in the way that they pass necessary
+information via carefully crafted job names. The job names are accessible via GitHub API, and they can be
+resolved during execution of the workflow using information about the linked workflow available
+at the workflow runtime. Hopefully this information will soon be available in GitHub Actions allowing
+removal of `namedJobs` cancel mode and simplifying the examples and workflows using the Action.
+
+You can take a look at the description provided in the
+[Apache Airflow's CI](https://github.com/apache/airflow/blob/master/CI.rst) and
+[the workflows](https://github.com/apache/airflow/blob/master/.github/workflows)
+
+Started from simple cancel workflow developed by [n1hility](https://github.com/n1hility)
+that implemented cancelling previous runs before introducing `workflow_run` type of event by
+GitHub Actions: [Cancel](https://github.com/n1hility/cancel-previous-runs).
+
+# Usage
+
+If you want a comprehensive solution, you should use the action as follows:
+
+1) In case your project does not use public forks, it's enough to have one action with the `duplicates`
+   cancel mode in the workflow. This is a rare thing in open-source projects (usually those projects
+   accept pull requests from forks) and more often applicable for private repositories.
+
+2) If you use forks, you should create a separate "Cancelling" `workflow_run` triggered workflow.
+   The `workflow_run` should be responsible for all canceling actions. The examples below show
+   the possible ways the action can be utilized.
+
+The previous version of this action (v1) used `schedule` events to cancel previous runs. With
+the announcement of pull_request_target by GitHub, the v2 version discourages using this pattern
+and `schedule` events are no longer needed.
+
+# Inputs and outputs
+
+## Inputs
+
+| Input            | Required | Default      | Comment                                                                                                                                                                                                          |
+|------------------|----------|--------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------|
+| `token`          | yes      |              | The github token passed from `${{ secrets.GITHUB_TOKEN }}`                                                                                                                                                       |
+| `cancelMode`     | no       | `duplicates` | The mode to run cancel on. The available options are `duplicates`, `self`, `failedJobs`, `namedJobs`                                                                                                             |
+| `sourceRunId`    | no       |              | Useful only in `workflow_run` triggered events. It should be set to the id of the workflow triggering the run `${{ github.event.workflow_run.id }}`  in case cancel operation should cancel the source workflow. |
+| `jobNameRegexps` | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+
+The job cancel modes work as follows:
+
+| Cancel Mode  | No `sourceRunId` specified                                             | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                      |
+|--------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| `duplicates` | Cancels past, duplicate runs from the same repo/branch as current run. | Cancels past, duplicate runs for the same repo/branch as the source run.            |
+| `self`       | Cancels self run.                                                      | Cancel the `sourceRunId` run.                                                       |
+| `failedJobs` | Cancels all runs of own workflow that have matching jobs that failed.  | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
+| `namedJobs`  | Cancels all runs of own workflow that have matching jobs.              | Cancels all runs of the `sourceRunId` workflow that have matching jobs.             |
+
+
+## Outputs
+
+| Output             | No `sourceRunId` specified                   | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                                      |
+|--------------------|----------------------------------------------|-----------------------------------------------------------------------------------------------------|
+| `sourceHeadRepo`   | Current repository. Format: `owner/repo`     | Repository of the run that triggered this `workflow_run`. Format: `owner/repo`                      |
+| `sourceHeadBranch` | Current branch.                              | Branch of the run that triggered this `workflow_run`. Might be forked repo, if it is a pull_requst. |
+| `sourceHeadSha`    | Current commit SHA: `{{ github.sha }}`       | Commit sha of the run that triggered this `workflow_run`.                                           |
+| `sourceEvent`      | Current event: ``${{ github.event }}``       | Event of the run that triggered this `workflow_run`                                                 |
+| `cancelledRuns`    | JSON-stringified array of cancelled run ids. | JSON-stringified array of cancelled run ids.                                                        |
+
+# Examples
+
+Note that you can combine the steps below in several steps of the same job. The examples here are showing
+one step per case for clarity.
+
+## Repositories that do not use Pull Requests from forks
+
+Note that examples in this chapter only work if you do not have Pull Requests coming from forks (so for
+example if you only work in a private repository). When those action runs within the usual `pull_request`
+triggered runs coming from a fork, they have not enough permissions to cancel running workflows.
+
+If you want to cancel `pull_requests` from forks, you need to use `workflow_run` triggered runs - see the
+[Repositories that use Pull Requests from fork](#repositories-that-use-pull-requests-from-forks) chapter.
+
+Note that in case you configure the separate `workflow_run` Cancelling workflow, there is no need to add
+the action to the "source" workflows. The "Canceling workflow" pattern handles well not only Pull Requests
+from the forks, but also all other cases - including cancelling Pull Requests for the same repository
+and canceling scheduled runs.
+
+### Cancel duplicate runs for "self" workflow
+
+Cancels past runs for the same workflow (with the same branch).
+
+In the case below, any of the direct "push" events will cancel all past runs for the same branch as the
+one being pushed. However, it can be configured for "pull_request" (in the same repository) or "schedule"
+type of events as well.
+
+```yaml
+name: CI
+on: push
+jobs:
+  cancel-duplicate-workflow-runs:
+    name: "Cancel duplicate workflow runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel duplicate workflow runs"
+        with:
+          cancelMode: duplicates
+          token: ${{ secrets.GITHUB_TOKEN }}
+```
+
+### Cancel "self" workflow run
+
+This is useful in case you decide to cancel "self" run.
+
+In the case below - own workflow will be cancelled immediately. It can be configured for "push",
+"pull_request" (from the same repository) or "schedule" type of events.
+
+```yaml
+name: CI
+on: push
+jobs:
+  cancel-self-run:
+    name: "Cancel the self workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - name: "Cancel the self workflow run"
+        uses: potiuk/cancel-workflow-runs@v2
+        with:
+          cancelMode: self
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+```
+
+### Fail-fast workflow runs with failed jobs
+
+Cancels all runs (including self run!) if they have failed jobs matching any of the regular expressions.
+Note that it does not take into account the branch of the running jobs. It will cancel all runs with failed
+jobs, no matter what branch originated it.
+
+In the case below, if any of the own workflow runs have failed jobs matching any of the
+`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
+
+```yaml
+name: CI
+on:
+  push:
+
+jobs:
+  cancel-self-failed-runs:
+    name: "Cancel failed runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel failed runs"
+        with:
+          cancelMode: failedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+```
+
+### Cancel all runs with named jobs
+
+Cancels all runs (including self run!) if any of the job names match any of the regular
+expressions. Note that it does not take into account the branch of the runs. It will cancel all runs with
+matching jobs, no matter what branch originated it.
+
+This is useful in case of job names generated dynamically.
+
+In the case below, if any of the "self" workflow runs has job names that matches any of the
+`^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp, this workflow will cancel the runs.
+
+```yaml
+on:
+  push:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-self-failed-runs:
+    name: "Cancel the self workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel past CI runs"
+        with:
+          cancelMode: namedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+```
+
+## Repositories that use Pull Requests from forks
+
+Note that in case you implement separate "Canceling workflow", following the examples below, you do not
+need to add cancel action to any other workflow. All Cancel actions should be configured in this
+Cancelling workflow.
+
+Those examples show how you should configure your project with separate `Cancelling` workflow which is
+triggered via `workflow_run` trigger.
+
+In the example belows we use the following names:
+
+* **triggered workflow** - the "Cancelling" workflow - separate workflow triggered by the `workflow_run`
+  event. Its main job is to manage cancelling of other workflows.
+
+* **triggered run** - the run of the *triggered workflow*. It is triggered by another ("source") run. In the
+  examples below, this run is in "Cancelling" workflow. It always runs in the context of the main repository,
+  even if it is triggered by a Pull Request from a fork.
+
+* **source workflow** - the "main" workflow - main workflow that performs CI actions. In the examples below,
+  this is a "CI" workflow.
+
+* **source run** - the run of the *source workflow*. It is the run that triggers the *triggered run*,
+  and it runs most of the CI tasks. In the examples below those are the runs of "CI" workflow.
+
+### Cancel duplicate runs for the source workflow
+
+Cancel past, duplicate *source runs* of the *source workflow*. This workflow cancels
+duplicated, past runs (for the same branch/repo that those associated with the *source run* that triggered
+the *triggered run*). You have to create it with the `sourceRunId` input with the value of
+`${{ github.event.workflow_run.id }}` in order to work correctly.
+
+In the example below, the `Canceling` run cancels past, duplicate runs from the `CI` with the same
+branch/repo as the *source run* which triggered it - effectively what's left after the action is only
+the latest *source run* of "CI" from the same branch/repo.
+
+This works for all kind of triggering events (`push`, `pull_request`, `schedule` ...). It works for
+events triggered in the local repository, as well as triggered from the forks, so you do not need
+to set up any extra actions to cancel internal Pushes/Pull Requests.
 
 ```yaml
-name: Cleanup Duplicate Branches and PRs
+name: Cancelling
 on:
-  schedule:
-    - cron:  '*/15 * * * *'
-cancel-runs:
-  # Prevent forks from running this to be nice
-  if: github.repository == 'foo-org/my-repo'
-  runs-on: ubuntu-latest
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-duplicate-workflow-runs:
+    name: "Cancel duplicate workflow runs"
+    runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v1
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel duplicate workflow runs"
         with:
+          cancelMode: duplicates
           token: ${{ secrets.GITHUB_TOKEN }}
-          workflow: my-heavy-workflow.yml
+          sourceRunId: ${{ github.event.workflow_run.id }}
 ```
 
+Note that `duplicate` cancel mode cannot be used for `workflow_run` type of event without `sourceId` input.
+The action will throw an error in this case because it is not really doing what you would expect it to do.
+All `workflow_run` events have the same branch and repository (they are all run in the context of the
+target branch and repository) no matter what is the source of the event, therefore cancelling duplicates
+would cancel all the runs originated from all the branches and this is not really expected.
+
+If you want to cancel duplicate runs of the *triggered workflow*, you need to utilize the
+`namedJob` cancel mode as described in the next chapter
+[Cancel duplicate jobs for triggered workflow](#cancel-duplicate-jobs-for-triggered-workflow) using outputs
+from the duplicate canceling for *source workflow* run above.
+
+Hopefully we will have an easier way of doing that in the future once GitHub Actions API will allow
+searching for source runs (it's not available at this moment).
+
+### Cancel duplicate jobs for triggered workflow
 
-### Schedule Example with fail-fast
+Cancels all past runs from the *triggered workflow* if any of the job names match any of the regular
+expressions. Note that it does not take into account the branch of the runs. It will cancel all runs
+with matching job names no mater the branch/repo.
+
+This example is much more complex. It shows the actual case on how you can design your jobs using with
+using outputs from the cancel duplicate action and running subsequent cancel with namedJobs cancel
+mode. Hopefully in the future better solution will come from Github Actions and such cancel flow will
+be natively supported by GitHub Actions but as of now (August 2020) such native support is not
+possible. The example below uses specially named jobs that contain Branch, Repo and Run id of
+the triggering run. The cancel operation finds the runs that have jobs with the names following
+pattern containing the same repo and branch as the source run branch and repo in order to cancel duplicates.
+
+In the case below, this workflow will first cancel the "CI" duplicate runs from the same branch and then
+it will cancel the runs from the Cancelling workflow which contain the same repo and branch as
+in job names, effectively implementing cancelling duplicate runs for the Cancelling workflow.
 
-This kills all previous runs of the workflow, and also latest run if one of the jobs
-matching `^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp failed in it.
 
 ```yaml
-name: Cleanup Duplicate Branches and fail-fast errors
+name: Cancelling
 on:
-  schedule:
-    - cron:  '*/15 * * * *'
-cancel-runs:
-  # Prevent forks from running this to be nice
-  if: github.repository == 'foo-org/my-repo'
-  runs-on: ubuntu-latest
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-duplicate-ci-runs:
+    name: "Cancel duplicate CI runs"
+    runs-on: ubuntu-latest
+    outputs:
+      sourceHeadRepo: ${{ steps.cancel.outputs.sourceHeadRepo }}
+      sourceHeadBranch: ${{ steps.cancel.outputs.sourceHeadBranch }}
+      sourceHeadSha: ${{ steps.cancel.outputs.sourceHeadSha }}
+      sourceEvent: ${{ steps.cancel.outputs.sourceEvent }}
     steps:
-      - uses: potiuk/cancel-workflow-runs@v1
+      - uses: potiuk/cancel-workflow-runs@v2
+        id: cancel
+        name: "Cancel duplicate CI runs"
+        with:
+          cancelMode: duplicates
+          token: ${{ secrets.GITHUB_TOKEN }}
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Cancel duplicate Cancelling runs"
         with:
+          cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
-          workflow: my-heavy-workflow.yml
-          failFastJobNames: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+          jobNameRegexps: >
+            ["Build info
+            repo: ${{ steps.cancel.outputs.sourceHeadRepo }}
+            branch: ${{ steps.cancel.outputs.sourceHeadBranch }}.*"]
+
+  build-info:
+    name: >
+      Build info
+      repo: ${{ needs.cancel-workflow-runs.outputs.sourceHeadRepo }}
+      branch: ${{ needs.cancel-workflow-runs.outputs.sourceHeadBranch }}
+    runs-on: ubuntu-latest
+    needs: [cancel-duplicate-ci-runs]
+    env:
+      GITHUB_CONTEXT: ${{ toJson(github) }}
+    steps:
+      - name: >
+          [${{ needs.cancel-workflow-runs.outputs.sourceEvent }}] will checkout
+          Run id: ${{ github.run_id }}
+          Source Run id: ${{ github.event.workflow_run.id }}
+          Sha: ${{ needs.cancel-workflow-runs.outputs.sourceHeadSha }}
+          Repo: ${{ needs.cancel-workflow-runs.outputs.sourceHeadRepo }}
+          Branch: ${{ needs.cancel-workflow-runs.outputs.sourceHeadBranch }}
+        run: |
+          printenv
 ```
 
 
-### Alternate/Fallback Example
+### Cancel the "self" source workflow run
+
+This is useful in case you decide to cancel the *source run* that triggered the *triggered run*.
+In the case below, the step cancels the `CI` workflow that triggered the `Cancelling` run.
 
 ```yaml
-  test:
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+  cancel-self-source-workflow-run:
+    name: "Cancel the self CI workflow run"
     runs-on: ubuntu-latest
     steps:
-    - uses: actions/checkout@v1
-    - uses: potiuk/cancel-workflow-runs@v1
-      with:
-        token: ${{ secrets.GITHUB_TOKEN }}
+      - name: "Cancel the self CI workflow run"
+        uses: potiuk/cancel-workflow-runs@v2
+        with:
+          cancelMode: self
+          token: ${{ secrets.GITHUB_TOKEN }}
+          sourceRunId: ${{ github.event.workflow_run.id }}
 ```
 
+
+### Cancel the "self" triggered workflow run
+
+This is useful in case you decide to cancel the *triggered run*. The difference vs. previous case is that
+you do not specify the `sourceRunId` input.
+
+In the case below - self workflow will be cancelled.
+
+```yaml
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+  cancel-self-cancelling-run:
+    name: "Cancel the self Canceling workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - name: "Cancel the self Cancelling workflow run"
+        uses: potiuk/cancel-workflow-runs@v2
+        with:
+          cancelMode: self
+          token: ${{ secrets.GITHUB_TOKEN }}
+
+```
+
+Note that if you want to cancel both - source workflow and self workflow you need to first cancel
+the source workflow, and then cancel the self one, not the other way round :).
+
+### Fail-fast source workflow runs with failed jobs
+
+Cancels all runs from the *source workflow* if there are failed jobs matching any of the regular expressions.
+Note that the action does not take into account the branch/repos of the runs. It will cancel all runs
+with failed jobs no mater the branch/repo.
+
+In the case below, if any of `CI` workflow runs (even with different branch heads) have failed jobs
+names matching `^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp - they
+will be cancelled.
+
+```yaml
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  fail-fast-triggered-workflow-named-jobs-runs:
+    name: "Fail fast CI runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Fail fast CI runs"
+        with:
+          cancelMode: failedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          sourceRunId: ${{ github.event.workflow_run.id }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+```
+
+Note that if you not only want to cancel the failed triggering workflows but also
+the want to fail the corresponding "Cancelling" workflows, you need to implement the solution
+described in the next chapter.
+
+### Fail-fast source workflow runs with failed jobs and corresponding triggered runs
+
+Cancels all runs from the *source workflow* if there are failed jobs matching any of the regular expressions,
+also cancels the corresponding *triggered runs*.
+Note that the action does not take into account the branch/repos of the runs. It will cancel all runs
+with failed jobs no mater the branch/repo.
+
+In the case below, if any of `CI` workflow runs (even with different branch heads) have failed jobs
+names matching `^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp - they
+will be cancelled as well as the corresponding "Cancelling" workflow runs.
+
+There is no native support yet in GitHub actions to do it easily, so the example below shows how this can be
+achieved using `namedJobs` and output returned from the previous `Cancel Workflow Runs` action. Hopefull
+this will be simplified when GitHub Actions introduce native support for it.
+
+```yaml
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  fail-fast-triggered-workflow-named-jobs-runs:
+    name: "Fail fast CI runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Fail fast CI. Source run: ${{ github.event.workflow_run.id }}"
+        id: cancel-failed
+        with:
+          cancelMode: failedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          sourceRunId: ${{ github.event.workflow_run.id }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+      - name: "Extract canceled failed runs"
+        id: extract-cancelled-failed-runs
+        if: steps.cancel-failed.outputs.cancelledRuns != "[]"
+        run: |
+            REGEXP="Fail fast CI. Source run: "
+            SEPARATOR=""
+            for run_id in $(echo "${{ steps.cancel-failed.outputs.cancelledRuns }}" | jq '.[]')
+            do
+                REGEXP="${REGEXP}${SEPARATOR}(${run_id})"
+                SEPARATOR="|"
+            done
+            echo "::set-output name=matching-regexp::${REGEXP}"
+      - name: "Cancel triggered 'Cancelling' runs for the cancelled failed runs"
+        if: steps.cancel-failed.outputs.cancelledRuns != "[]"
+        uses: potiuk/cancel-workflow-runs@master
+        with:
+          cancelMode: namedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: ${{ steps.extract-cancelled-failed.runs.matching-regexp }}
+
+```
+
+Note that if you not only want to cancel the failed triggering workflows but also
+the want to fail the corresponding "Cancelling" workflows, you need to implement the solution
+described in the next chapter.
+
+### Fail-fast for triggered workflow runs with failed jobs
+
+Cancels all runs from the *triggered workflow* if there are failed jobs matching any of the regular
+expressions. Note that it does not take into account the branch/repos of the runs. It will cancel all runs
+with failed jobs no mater the branch/repo.
+
+In the case below, if any of `Cancelling` workflow runs (even with different branch heads) have failed jobs
+names matching `^Static checks$` and `^Build docs^` or `^Build prod image .*` regexp - they
+will be cancelled.
+
+```yaml
+name: Cancelling
+on:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  fail-fast-triggered-workflow-named-jobs-runs:
+    name: "Fail fast Canceling runs"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@v2
+        name: "Fail fast Canceling runs"
+        with:
+          cancelMode: failedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["^Static checks$", "^Build docs$", "^Build prod image.*"]'
+```
+
+
+## Development environment
+
+It is highly recommended tu use [pre commit](https://pre-commit.com). The pre-commits
+installed via pre-commit tool handle automatically linting (including automated fixes) as well
+as building and packaging Javascript index.js from the main.ts Typescript code, so you do not have
+to run it yourself.
+
 ## License
 [MIT License](LICENSE) covers the scripts and documentation in this project.
diff --git a/action.yml b/action.yml
index 1b2515d..71f6ae2 100644
--- a/action.yml
+++ b/action.yml
@@ -1,20 +1,34 @@
 name: 'Cancel Workflow Runs'
-description: 'Cancels previous runs and optionally failed runs of a workflow'
+description: 'Cancel Workflow Runs - duplicates, failed, named in order to limit job usage,'
 author: 'potiuk'
 inputs:
   token:
     description: The GITHUB_TOKEN secret of the repository
     required: true
-  workflow:
-    description: The filename of the workflow to limit runs on (only applies to scheduled events)
+  sourceRunId:
+    description: |
+      The run that triggered the action. It should be set to
+      `$\{\{ github.event.workflow_run.id` variable \}\}` if used in `workflow_run` triggered run if
+      you want to act on source workflow rather than the triggered run.
+    required: false
+  cancelMode:
+    description: |
+      The mode of cancel. One of:
+          * `duplicates`  - cancels past, duplicate runs from the same repo/branch as local run or
+                            sourceId workflow. This is the default mode when cancelMode is not specified.
+          * `self`        - cancels self run - either own run if sourceRunId is not set, or
+                            the source run that triggered the `workflow_run'
+          * `failedJobs`  - cancels all runs that failed in jobs matching one of the regexps
+          * `namedJobs`   - cancels runs where names of some jobs match some of regexps
     required: false
-  failFastJobNames:
+  jobNameRegexps:
     description: |
-      Array of job names (JSON-encoded string). Failures of those jobs are fail-fast of whole workflow.
+      Array of job name regexps (JSON-encoded string). Used by `failedJobs` and `namedJobs` cancel modes
+      to match job names of workflow runs.
     required: false
 runs:
   using: 'node12'
   main: 'dist/index.js'
 branding:
   icon: 'play'
-  color: 'orange'
+  color: 'blue'
diff --git a/dist/index.js b/dist/index.js
index 302be03..2a8c75f 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -34,7 +34,7 @@ module.exports =
 /******/ 	// the startup function
 /******/ 	function startup() {
 /******/ 		// Load entry module and return exports
-/******/ 		return __webpack_require__(198);
+/******/ 		return __webpack_require__(131);
 /******/ 	};
 /******/
 /******/ 	// run startup
@@ -1434,6 +1434,426 @@ module.exports = require("child_process");
 
 /***/ }),
 
+/***/ 131:
+/***/ (function(__unusedmodule, exports, __webpack_require__) {
+
+"use strict";
+
+var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
+    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
+    return new (P || (P = Promise))(function (resolve, reject) {
+        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
+        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
+        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
+        step((generator = generator.apply(thisArg, _arguments || [])).next());
+    });
+};
+var __asyncValues = (this && this.__asyncValues) || function (o) {
+    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
+    var m = o[Symbol.asyncIterator], i;
+    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
+    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
+    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
+};
+var __importStar = (this && this.__importStar) || function (mod) {
+    if (mod && mod.__esModule) return mod;
+    var result = {};
+    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
+    result["default"] = mod;
+    return result;
+};
+Object.defineProperty(exports, "__esModule", { value: true });
+const github = __importStar(__webpack_require__(469));
+const core = __importStar(__webpack_require__(393));
+const treemap = __importStar(__webpack_require__(706));
+const CANCELLABLE_RUNS = ['push', 'pull_request', 'workflow_run', 'schedule'];
+var CancelMode;
+(function (CancelMode) {
+    CancelMode["DUPLICATES"] = "duplicates";
+    CancelMode["SELF"] = "self";
+    CancelMode["FAILED_JOBS"] = "failedJobs";
+    CancelMode["NAMED_JOBS"] = "namedJobs";
+})(CancelMode || (CancelMode = {}));
+function createListRunsQueryOtherRuns(octokit, owner, repo, status, workflowId, headBranch, eventName) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        workflow_id: workflowId,
+        status,
+        branch: headBranch,
+        event: eventName
+    };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
+function createListRunsQueryMyOwnRun(octokit, owner, repo, status, workflowId, runId) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        workflow_id: workflowId,
+        status,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        run_id: runId.toString()
+    };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
+function createListRunsQueryAllRuns(octokit, owner, repo, status, workflowId) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        workflow_id: workflowId,
+        status
+    };
+    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
+}
+function createJobsForWorkflowRunQuery(octokit, owner, repo, runId) {
+    const request = {
+        owner,
+        repo,
+        // eslint-disable-next-line @typescript-eslint/camelcase
+        run_id: runId
+    };
+    return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request);
+}
+function matchInArray(s, regexps) {
+    for (const regexp of regexps) {
+        if (s.match(regexp)) {
+            return true;
+        }
+    }
+    return false;
+}
+function jobsMatchingNames(octokit, owner, repo, runId, jobNameRegexps, checkIfFailed) {
+    var e_1, _a;
+    return __awaiter(this, void 0, void 0, function* () {
+        const listJobs = createJobsForWorkflowRunQuery(octokit, owner, repo, runId);
+        if (checkIfFailed) {
+            core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps} that failed\n`);
+        }
+        else {
+            core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`);
+        }
+        try {
+            for (var _b = __asyncValues(octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
+                const item = _c.value;
+                for (const job of item.data.jobs) {
+                    core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`);
+                    if (matchInArray(job.name, jobNameRegexps)) {
+                        if (checkIfFailed) {
+                            // Only fail the build if one of the matching jobs fail
+                            if (job.conclusion === 'failure') {
+                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed. Cancelling run.`);
+                                return true;
+                            }
+                            else {
+                                core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. So far, so good.`);
+                            }
+                        }
+                        else {
+                            // Fail the build if any of the job names match
+                            core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. Cancelling run.`);
+                            return true;
+                        }
+                    }
+                }
+            }
+        }
+        catch (e_1_1) { e_1 = { error: e_1_1 }; }
+        finally {
+            try {
+                if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+            }
+            finally { if (e_1) throw e_1.error; }
+        }
+        return false;
+    });
+}
+function getWorkflowId(octokit, runId, owner, repo) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const reply = yield octokit.actions.getWorkflowRun({
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            run_id: runId
+        });
+        core.info(`The source run ${runId} is in ${reply.data.workflow_url} workflow`);
+        const workflowIdString = reply.data.workflow_url.split('/').pop() || '';
+        if (!(workflowIdString.length > 0)) {
+            throw new Error('Could not resolve workflow');
+        }
+        return parseInt(workflowIdString);
+    });
+}
+function getWorkflowRuns(octokit, statusValues, cancelMode, createListRunQuery) {
+    var e_2, _a;
+    return __awaiter(this, void 0, void 0, function* () {
+        const workflowRuns = new treemap.TreeMap();
+        for (const status of statusValues) {
+            const listRuns = yield createListRunQuery(status);
+            try {
+                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
+                    const item = _c.value;
+                    // There is some sort of bug where the pagination URLs point to a
+                    // different endpoint URL which trips up the resulting representation
+                    // In that case, fallback to the actual REST 'workflow_runs' property
+                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
+                    for (const element of elements) {
+                        workflowRuns.set(element.run_number, element);
+                    }
+                }
+            }
+            catch (e_2_1) { e_2 = { error: e_2_1 }; }
+            finally {
+                try {
+                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
+                }
+                finally { if (e_2) throw e_2.error; }
+            }
+        }
+        core.info(`\nFound runs: ${Array.from(workflowRuns).map(t => t[0])}\n`);
+        return workflowRuns;
+    });
+}
+function shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNamesRegexps) {
+    return __awaiter(this, void 0, void 0, function* () {
+        if ('completed' === runItem.status.toString()) {
+            core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
+            return false;
+        }
+        if (!CANCELLABLE_RUNS.includes(runItem.event.toString())) {
+            core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not in ${CANCELLABLE_RUNS}). Not cancelling it.\n`);
+            return false;
+        }
+        if (cancelMode === CancelMode.FAILED_JOBS) {
+            // Cancel all jobs that have failed jobs (no matter when started)
+            if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, true)) {
+                core.info(`\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`);
+                return true;
+            }
+            else {
+                core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`);
+                return false;
+            }
+        }
+        else if (cancelMode === CancelMode.NAMED_JOBS) {
+            // Cancel all jobs that have failed jobs (no matter when started)
+            if (yield jobsMatchingNames(octokit, owner, repo, runItem.id, jobNamesRegexps, false)) {
+                core.info(`\nSome jobs have matching names in ${runItem.id} . Cancelling it.\n`);
+                return true;
+            }
+            else {
+                core.info(`\nNone of the jobs match name in ${runItem.id}. Not cancelling it.\n`);
+                return false;
+            }
+        }
+        else if (cancelMode === CancelMode.SELF) {
+            if (runItem.id === sourceRunId) {
+                core.info(`\nCancelling the "source" run: ${runItem.id}.\n`);
+                return true;
+            }
+            else {
+                return false;
+            }
+        }
+        else if (cancelMode === CancelMode.DUPLICATES) {
+            const runHeadRepo = runItem.head_repository.full_name;
+            if (headRepo !== undefined && runHeadRepo !== headRepo) {
+                core.info(`\nThe run ${runItem.id} is from a different ` +
+                    `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
+                return false;
+            }
+            if (runItem.id === sourceRunId) {
+                core.info(`\nThis is my own run ${runItem.id}. I have self-preservation mechanism. Not cancelling myself!\n`);
+                return false;
+            }
+            else if (runItem.id > sourceRunId) {
+                core.info(`\nThe run ${runItem.id} is started later than mt own run ${sourceRunId}. Not cancelling it\n`);
+                return false;
+            }
+            else {
+                core.info(`\nCancelling duplicate of my own run: ${runItem.id}.\n`);
+                return true;
+            }
+        }
+        else {
+            throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+        }
+    });
+}
+function cancelRun(octokit, owner, repo, runId) {
+    return __awaiter(this, void 0, void 0, function* () {
+        let reply;
+        try {
+            reply = yield octokit.actions.cancelWorkflowRun({
+                owner,
+                repo,
+                // eslint-disable-next-line @typescript-eslint/camelcase
+                run_id: runId
+            });
+            core.info(`\nThe run ${runId} cancelled, status = ${reply.status}\n`);
+        }
+        catch (error) {
+            core.warning(`\nCould not cancel run ${runId}: [${error.status}] ${error.message}\n`);
+        }
+    });
+}
+function findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const statusValues = ['queued', 'in_progress'];
+        const workflowRuns = yield getWorkflowRuns(octokit, statusValues, cancelMode, function (status) {
+            if (cancelMode === CancelMode.SELF) {
+                core.info(`\nFinding runs for my own run: Owner: ${owner}, Repo: ${repo}, ` +
+                    `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`);
+                return createListRunsQueryMyOwnRun(octokit, owner, repo, status, sourceWorkflowId, sourceRunId);
+            }
+            else if (cancelMode === CancelMode.FAILED_JOBS ||
+                cancelMode === CancelMode.NAMED_JOBS) {
+                core.info(`\nFinding runs for all runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
+                    `Workflow ID:${sourceWorkflowId}\n`);
+                return createListRunsQueryAllRuns(octokit, owner, repo, status, sourceWorkflowId);
+            }
+            else if (cancelMode === CancelMode.DUPLICATES) {
+                core.info(`\nFinding duplicate runs: Owner: ${owner}, Repo: ${repo}, Status: ${status} ` +
+                    `Workflow ID:${sourceWorkflowId}, Head Branch: ${headBranch},` +
+                    `Event name: ${sourceEventName}\n`);
+                return createListRunsQueryOtherRuns(octokit, owner, repo, status, sourceWorkflowId, headBranch, sourceEventName);
+            }
+            else {
+                throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+            }
+        });
+        const idsToCancel = [];
+        for (const [key, runItem] of workflowRuns) {
+            core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status}\n`);
+            if (yield shouldBeCancelled(octokit, owner, repo, runItem, headRepo, cancelMode, sourceRunId, jobNameRegexps)) {
+                idsToCancel.push(runItem.id);
+            }
+        }
+        // Sort from smallest number - this way we always kill current one at the end (if we kill it at all)
+        const sortedIdsToCancel = idsToCancel.sort((id1, id2) => id1 - id2);
+        if (sortedIdsToCancel.length > 0) {
+            core.info('\n######  Cancelling runs starting from the oldest  ##########\n' +
+                `\n     Runs to cancel: ${sortedIdsToCancel.length}\n`);
+            for (const runId of sortedIdsToCancel) {
+                core.info(`\nCancelling run: ${runId}.\n`);
+                yield cancelRun(octokit, owner, repo, runId);
+            }
+            core.info('\n######  Finished cancelling runs                  ##########\n');
+        }
+        else {
+            core.info('\n######  There are no runs to cancel!              ##########\n');
+        }
+        return sortedIdsToCancel;
+    });
+}
+function getRequiredEnv(key) {
+    const value = process.env[key];
+    if (value === undefined) {
+        const message = `${key} was not defined.`;
+        throw new Error(message);
+    }
+    return value;
+}
+function getOrigin(octokit, runId, owner, repo) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const reply = yield octokit.actions.getWorkflowRun({
+            owner,
+            repo,
+            // eslint-disable-next-line @typescript-eslint/camelcase
+            run_id: runId
+        });
+        const sourceRun = reply.data;
+        core.info(`Source workflow: Head repo: ${sourceRun.head_repository.full_name}, ` +
+            `Head branch: ${sourceRun.head_branch} ` +
+            `Event: ${sourceRun.event}, Head sha: ${sourceRun.head_sha}, url: ${sourceRun.url}`);
+        return [
+            reply.data.head_repository.full_name,
+            reply.data.head_branch,
+            reply.data.event,
+            reply.data.head_sha
+        ];
+    });
+}
+function performCancelJob(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps) {
+    return __awaiter(this, void 0, void 0, function* () {
+        core.info('\n###################################################################################\n');
+        core.info(`All parameters: owner: ${owner}, repo: ${repo}, run id: ${sourceRunId}, ` +
+            `head repo ${headRepo}, headBranch: ${headBranch}, ` +
+            `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
+        core.info('\n###################################################################################\n');
+        if (cancelMode === CancelMode.SELF) {
+            core.info(`# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+        }
+        else if (cancelMode === CancelMode.FAILED_JOBS) {
+            core.info(`# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`);
+        }
+        else if (cancelMode === CancelMode.NAMED_JOBS) {
+            core.info(`# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`);
+        }
+        else if (cancelMode === CancelMode.DUPLICATES) {
+            core.info(`# Cancel duplicate runs started before ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+        }
+        else {
+            throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
+        }
+        core.info('\n###################################################################################\n');
+        return yield findAndCancelRuns(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps);
+    });
+}
+function run() {
+    return __awaiter(this, void 0, void 0, function* () {
+        const token = core.getInput('token', { required: true });
+        const octokit = new github.GitHub(token);
+        const selfRunId = parseInt(getRequiredEnv('GITHUB_RUN_ID'));
+        const repository = getRequiredEnv('GITHUB_REPOSITORY');
+        const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
+        const cancelMode = core.getInput('cancelMode') || CancelMode.DUPLICATES;
+        const sourceRunId = parseInt(core.getInput('sourceRunId')) || selfRunId;
+        const jobNameRegexpsString = core.getInput('jobNameRegexps');
+        const jobNameRegexps = jobNameRegexpsString
+            ? JSON.parse(jobNameRegexpsString)
+            : [];
+        const [owner, repo] = repository.split('/');
+        core.info(`\nGetting workflow id for source run id: ${sourceRunId}, owner: ${owner}, repo: ${repo}\n`);
+        const sourceWorkflowId = yield getWorkflowId(octokit, sourceRunId, owner, repo);
+        core.info(`Repository: ${repository}, Owner: ${owner}, Repo: ${repo}, ` +
+            `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
+            `sourceWorkflowId: ${sourceWorkflowId}, sourceRunId: ${sourceRunId}, selfRunId: ${selfRunId}, ` +
+            `jobNames: ${jobNameRegexps}`);
+        if (sourceRunId === selfRunId) {
+            core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
+        }
+        else {
+            core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
+        }
+        if (jobNameRegexps.length > 0 &&
+            [CancelMode.DUPLICATES, CancelMode.SELF].includes(cancelMode)) {
+            throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
+        }
+        if (eventName === 'workflow_run' && sourceRunId === selfRunId) {
+            if (cancelMode === CancelMode.DUPLICATES)
+                throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
+                    'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
+                    'See the docs for details.');
+        }
+        const [headRepo, headBranch, sourceEventName, headSha] = yield getOrigin(octokit, sourceRunId, owner, repo);
+        core.setOutput('sourceHeadRepo', headRepo);
+        core.setOutput('sourceHeadBranch', headBranch);
+        core.setOutput('sourceHeadSha', headSha);
+        core.setOutput('sourceEvent', sourceEventName);
+        const cancelledRuns = yield performCancelJob(octokit, sourceWorkflowId, sourceRunId, owner, repo, headRepo, headBranch, sourceEventName, cancelMode, jobNameRegexps);
+        core.setOutput('cancelledRuns', JSON.stringify(cancelledRuns));
+    });
+}
+run()
+    .then(() => core.info('\n############### Cancel complete ##################\n'))
+    .catch(e => core.setFailed(e.message));
+
+
+/***/ }),
+
 /***/ 141:
 /***/ (function(__unusedmodule, exports, __webpack_require__) {
 
@@ -1988,311 +2408,6 @@ function checkMode (stat, options) {
 
 /***/ }),
 
-/***/ 198:
-/***/ (function(__unusedmodule, exports, __webpack_require__) {
-
-"use strict";
-
-var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
-    function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
-    return new (P || (P = Promise))(function (resolve, reject) {
-        function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
-        function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
-        function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
-        step((generator = generator.apply(thisArg, _arguments || [])).next());
-    });
-};
-var __asyncValues = (this && this.__asyncValues) || function (o) {
-    if (!Symbol.asyncIterator) throw new TypeError("Symbol.asyncIterator is not defined.");
-    var m = o[Symbol.asyncIterator], i;
-    return m ? m.call(o) : (o = typeof __values === "function" ? __values(o) : o[Symbol.iterator](), i = {}, verb("next"), verb("throw"), verb("return"), i[Symbol.asyncIterator] = function () { return this; }, i);
-    function verb(n) { i[n] = o[n] && function (v) { return new Promise(function (resolve, reject) { v = o[n](v), settle(resolve, reject, v.done, v.value); }); }; }
-    function settle(resolve, reject, d, v) { Promise.resolve(v).then(function(v) { resolve({ value: v, done: d }); }, reject); }
-};
-var __importStar = (this && this.__importStar) || function (mod) {
-    if (mod && mod.__esModule) return mod;
-    var result = {};
-    if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result[k] = mod[k];
-    result["default"] = mod;
-    return result;
-};
-Object.defineProperty(exports, "__esModule", { value: true });
-const github = __importStar(__webpack_require__(469));
-const core = __importStar(__webpack_require__(393));
-const treemap = __importStar(__webpack_require__(706));
-function createListRunsQueryForAllRuns(octokit, owner, repo, workflowId, status) {
-    const request = {
-        owner,
-        repo,
-        // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: workflowId,
-        status
-    };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
-}
-function createListRunsQueryForSelfRun(octokit, owner, repo, workflowId, status, branch, eventName) {
-    const request = {
-        owner,
-        repo,
-        // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: workflowId,
-        status,
-        branch,
-        event: eventName
-    };
-    return octokit.actions.listWorkflowRuns.endpoint.merge(request);
-}
-function createJobsForWorkflowRunQuery(octokit, owner, repo, runId) {
-    const request = {
-        owner,
-        repo,
-        // eslint-disable-next-line @typescript-eslint/camelcase
-        run_id: runId,
-    };
-    return octokit.actions.listJobsForWorkflowRun.endpoint.merge(request);
-}
-function cancelOnFailFastJobsFailed(octokit, owner, repo, runId, head, failFastJobNames) {
-    var e_1, _a;
-    return __awaiter(this, void 0, void 0, function* () {
-        const listJobs = createJobsForWorkflowRunQuery(octokit, owner, repo, runId);
-        core.info(`Cancelling runId ${runId} in case one of the ${failFastJobNames} failed`);
-        try {
-            for (var _b = __asyncValues(octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
-                const item = _c.value;
-                for (const job of item.data.jobs) {
-                    core.info(`The job name: ${job.name}, Conclusion: ${job.conclusion}`);
-                    if (job.conclusion == 'failure' &&
-                        failFastJobNames.some(jobNameRegexp => job.name.match(jobNameRegexp))) {
-                        core.info(`Job ${job.name} has failed and it matches one of the ${failFastJobNames} regexps`);
-                        core.info(`Cancelling the workflow run: ${runId}, head: ${head}`);
-                        yield cancelRun(octokit, owner, repo, runId);
-                        return;
-                    }
-                }
-            }
-        }
-        catch (e_1_1) { e_1 = { error: e_1_1 }; }
-        finally {
-            try {
-                if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
-            }
-            finally { if (e_1) throw e_1.error; }
-        }
-    });
-}
-function getSelfWorkflowId(octokit, selfRunId, owner, repo) {
-    return __awaiter(this, void 0, void 0, function* () {
-        let workflowId;
-        const reply = yield octokit.actions.getWorkflowRun({
-            owner,
-            repo,
-            // eslint-disable-next-line @typescript-eslint/camelcase
-            run_id: Number.parseInt(selfRunId)
-        });
-        workflowId = reply.data.workflow_url.split('/').pop() || '';
-        if (!(workflowId.length > 0)) {
-            throw new Error('Could not resolve workflow');
-        }
-        return workflowId;
-    });
-}
-function getSortedWorkflowRuns(octokit, createListRunQuery) {
-    var e_2, _a;
-    return __awaiter(this, void 0, void 0, function* () {
-        const sortedWorkflowRuns = new treemap.TreeMap();
-        for (const status of ['queued', 'in_progress']) {
-            const listRuns = yield createListRunQuery(status);
-            try {
-                for (var _b = __asyncValues(octokit.paginate.iterator(listRuns)), _c; _c = yield _b.next(), !_c.done;) {
-                    const item = _c.value;
-                    // There is some sort of bug where the pagination URLs point to a
-                    // different endpoint URL which trips up the resulting representation
-                    // In that case, fallback to the actual REST 'workflow_runs' property
-                    const elements = item.data.length === undefined ? item.data.workflow_runs : item.data;
-                    for (const element of elements) {
-                        sortedWorkflowRuns.set(element.run_number, element);
-                    }
-                }
-            }
-            catch (e_2_1) { e_2 = { error: e_2_1 }; }
-            finally {
-                try {
-                    if (_c && !_c.done && (_a = _b.return)) yield _a.call(_b);
-                }
-                finally { if (e_2) throw e_2.error; }
-            }
-        }
-        core.info(`Found runs: ${Array.from(sortedWorkflowRuns.backward()).map(t => t[0])}`);
-        return sortedWorkflowRuns;
-    });
-}
-function shouldRunBeSkipped(runItem) {
-    if ('completed' === runItem.status.toString()) {
-        core.info(`Skip completed run: ${runItem.id}`);
-        return true;
-    }
-    if (!['push', 'pull_request'].includes(runItem.event.toString())) {
-        core.info(`Skip run: ${runItem.id} as it is neither push nor pull_request (${runItem.event}`);
-        return true;
-    }
-    return false;
-}
-function cancelRun(octokit, owner, repo, id) {
-    return __awaiter(this, void 0, void 0, function* () {
-        let reply;
-        try {
-            reply = yield octokit.actions.cancelWorkflowRun({
-                owner: owner,
-                repo: repo,
-                // eslint-disable-next-line @typescript-eslint/camelcase
-                run_id: id
-            });
-            core.info(`Previous run (id ${id}) cancelled, status = ${reply.status}`);
-        }
-        catch (error) {
-            core.info(`[warn] Could not cancel run (id ${id}): [${error.status}] ${error.message}`);
-        }
-    });
-}
-// Kills past runs for my own workflow.
-function findAndCancelPastRunsForSelf(octokit, selfRunId, owner, repo, branch, eventName) {
-    return __awaiter(this, void 0, void 0, function* () {
-        core.info(`findAndCancelPastRunsForSelf:  ${selfRunId}, ${owner}, ${repo}, ${branch}, ${eventName}`);
-        const workflowId = yield getSelfWorkflowId(octokit, selfRunId, owner, repo);
-        core.info(`My own workflow ID is: ${workflowId}`);
-        const sortedWorkflowRuns = yield getSortedWorkflowRuns(octokit, function (status) {
-            return createListRunsQueryForSelfRun(octokit, owner, repo, workflowId, status, branch, eventName);
-        });
-        let matched = false;
-        const headsToRunIdMap = new Map();
-        for (const [key, runItem] of sortedWorkflowRuns.backward()) {
-            core.info(`Run number: ${key}, RunId: ${runItem.id}, URL: ${runItem.workflow_url}. Status ${runItem.status}`);
-            if (!matched) {
-                if (runItem.id.toString() !== selfRunId) {
-                    core.info(`Skip run ${runItem.id} as it was started before my own id: ${selfRunId}`);
-                    continue;
-                }
-                matched = true;
-                core.info(`Matched ${selfRunId}. Reached my own ID, now looping through all remaining runs/`);
-                core.info("I will cancel all except the first for each 'head' available");
-            }
-            if (shouldRunBeSkipped(runItem)) {
-                continue;
-            }
-            // Head of the run
-            const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`;
-            if (!headsToRunIdMap.has(head)) {
-                core.info(`First run for the head: ${head}. Skipping it. Next ones with same head will be cancelled.`);
-                headsToRunIdMap.set(head, runItem.id);
-                continue;
-            }
-            core.info(`Cancelling run: ${runItem.id}, head ${head}.`);
-            core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`);
-            yield cancelRun(octokit, owner, repo, runItem.id);
-        }
-    });
-}
-// Kills past runs for my own workflow.
-function findAndCancelPastRunsForSchedule(octokit, workflowId, owner, repo, failFastJobNames) {
-    return __awaiter(this, void 0, void 0, function* () {
-        core.info(`findAndCancelPastRunsForSchedule: ${owner}, ${workflowId}, ${repo}`);
-        const sortedWorkflowRuns = yield getSortedWorkflowRuns(octokit, function (status) {
-            return createListRunsQueryForAllRuns(octokit, owner, repo, workflowId, status);
-        });
-        const headsToRunIdMap = new Map();
-        for (const [key, runItem] of sortedWorkflowRuns.backward()) {
-            core.info(` ${key} ${runItem.id} : ${runItem.workflow_url} : ${runItem.status} : ${runItem.run_number}`);
-            if (shouldRunBeSkipped(runItem)) {
-                continue;
-            }
-            // Head of the run
-            const head = `${runItem.head_repository.full_name}/${runItem.head_branch}`;
-            if (!headsToRunIdMap.has(head)) {
-                core.info(`First run for the head: ${head}. Next runs with the same head will be cancelled.`);
-                headsToRunIdMap.set(head, runItem.id);
-                if (failFastJobNames !== undefined) {
-                    core.info("Checking if the head run failed in specified jobs");
-                    yield cancelOnFailFastJobsFailed(octokit, owner, repo, runItem.id, head, failFastJobNames);
-                }
-                else {
-                    core.info("Skipping the head run.");
-                }
-                continue;
-            }
-            core.info(`Cancelling run: ${runItem.id}, head ${head}.`);
-            core.info(`There is a later run with same head: ${headsToRunIdMap.get(head)}`);
-            yield cancelRun(octokit, owner, repo, runItem.id);
-        }
-    });
-}
-function getRequiredEnv(key) {
-    const value = process.env[key];
-    if (value === undefined) {
-        const message = `${key} was not defined.`;
-        throw new Error(message);
-    }
-    return value;
-}
-function runScheduledRun(octokit, owner, repo) {
-    return __awaiter(this, void 0, void 0, function* () {
-        const workflowId = core.getInput('workflow');
-        if (!(workflowId.length > 0)) {
-            core.setFailed('Workflow must be specified for schedule event type');
-            return;
-        }
-        const failFastJobNames = JSON.parse(core.getInput('failFastJobNames'));
-        if (failFastJobNames !== undefined) {
-            core.info(`Checking also if last run failed in one of the jobs: ${failFastJobNames}`);
-        }
-        yield findAndCancelPastRunsForSchedule(octokit, workflowId, owner, repo, failFastJobNames);
-        return;
-    });
-}
-function runRegularRun(octokit, selfRunId, owner, repo, eventName) {
-    return __awaiter(this, void 0, void 0, function* () {
-        const pullRequest = 'pull_request' === eventName;
-        const branchPrefix = 'refs/heads/';
-        const tagPrefix = 'refs/tags/';
-        let branch = getRequiredEnv(pullRequest ? 'GITHUB_HEAD_REF' : 'GITHUB_REF');
-        if (!pullRequest && !branch.startsWith(branchPrefix)) {
-            if (branch.startsWith(tagPrefix)) {
-                core.info(`Skipping tag build`);
-                return;
-            }
-            core.setFailed(`${branch} was not an expected branch ref (refs/heads/).`);
-            return;
-        }
-        branch = branch.replace(branchPrefix, '');
-        core.info(`Branch is ${branch}, repo is ${repo}, and owner is ${owner}, and id is ${selfRunId}`);
-        yield findAndCancelPastRunsForSelf(octokit, selfRunId, owner, repo, branch, eventName);
-    });
-}
-function run() {
-    return __awaiter(this, void 0, void 0, function* () {
-        const token = core.getInput('token');
-        const octokit = new github.GitHub(token);
-        core.info(`Starting checking for workflows to cancel`);
-        const selfRunId = getRequiredEnv('GITHUB_RUN_ID');
-        const repository = getRequiredEnv('GITHUB_REPOSITORY');
-        const eventName = getRequiredEnv('GITHUB_EVENT_NAME');
-        const [owner, repo] = repository.split('/');
-        if ('schedule' === eventName) {
-            yield runScheduledRun(octokit, owner, repo);
-        }
-        else if (!['push', 'pull_request'].includes(eventName)) {
-            core.info('Skipping unsupported event');
-            return;
-        }
-        else {
-            yield runRegularRun(octokit, selfRunId, owner, repo, eventName);
-        }
-    });
-}
-run().then(() => core.info("Cancel complete")).catch(e => core.setFailed(e.message));
-
-
-/***/ }),
-
 /***/ 211:
 /***/ (function(module) {
 
@@ -2303,7 +2418,7 @@ module.exports = require("https");
 /***/ 215:
 /***/ (function(module) {
 
-module.exports = {"_args":[["@octokit/rest@16.43.0","/home/jarek/code/cancel-previous-runs"]],"_from":"@octokit/rest@16.43.0","_id":"@octokit/rest@16.43.0","_inBundle":false,"_integrity":"sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==","_location":"/@octokit/rest","_phantomChildren":{},"_requested":{"type":"version","registry":true,"raw":"@octokit/rest@16.43.0","name":"@octokit/rest","escapedName":"@octokit%2frest","scope":"@octokit","rawS [...]
+module.exports = {"name":"@octokit/rest","version":"16.43.0","publishConfig":{"access":"public"},"description":"GitHub REST API client for Node.js","keywords":["octokit","github","rest","api-client"],"author":"Gregor Martynus (https://github.com/gr2m)","contributors":[{"name":"Mike de Boer","email":"info@mikedeboer.nl"},{"name":"Fabian Jakobs","email":"fabian@c9.io"},{"name":"Joe Gallo","email":"joe@brassafrax.com"},{"name":"Gregor Martynus","url":"https://github.com/gr2m"}],"repository" [...]
 
 /***/ }),
 
diff --git a/jest.config.js b/jest.config.js
index 563d4cc..14e44f9 100644
--- a/jest.config.js
+++ b/jest.config.js
@@ -8,4 +8,4 @@ module.exports = {
     '^.+\\.ts$': 'ts-jest'
   },
   verbose: true
-}
\ No newline at end of file
+}
diff --git a/package-lock.json b/package-lock.json
index bfeb285..f5e72b5 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -4,6 +4,28 @@
   "lockfileVersion": 1,
   "requires": true,
   "dependencies": {
+    "@actions/artifact": {
+      "version": "0.3.5",
+      "resolved": "https://registry.npmjs.org/@actions/artifact/-/artifact-0.3.5.tgz",
+      "integrity": "sha512-y27pBEnUjOqCP2zUf86YkiqGOp1r0C9zUOmGmcxizsHMls0wvk+FJwd+l8JIoukvj1BeBHYP+c+9AEqOt5AqMA==",
+      "requires": {
+        "@actions/core": "^1.2.1",
+        "@actions/http-client": "^1.0.7",
+        "@types/tmp": "^0.1.0",
+        "tmp": "^0.1.0",
+        "tmp-promise": "^2.0.2"
+      },
+      "dependencies": {
+        "tmp": {
+          "version": "0.1.0",
+          "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.1.0.tgz",
+          "integrity": "sha512-J7Z2K08jbGcdA1kkQpJSqLF6T0tdQqpR2pnSUXsIchbPdTI9v3e85cLW0d6WDhwuAleOV71j2xWs8qMPfK7nKw==",
+          "requires": {
+            "rimraf": "^2.6.3"
+          }
+        }
+      }
+    },
     "@actions/core": {
       "version": "1.2.2",
       "resolved": "https://registry.npmjs.org/@actions/core/-/core-1.2.2.tgz",
@@ -27,6 +49,12 @@
         "tunnel": "0.0.6"
       }
     },
+    "@ardatan/aggregate-error": {
+      "version": "0.0.1",
+      "resolved": "https://registry.npmjs.org/@ardatan/aggregate-error/-/aggregate-error-0.0.1.tgz",
+      "integrity": "sha512-UQ9BequOTIavs0pTHLMwQwKQF8tTV1oezY/H2O9chA+JNPFZSua55xpU5dPSjAU9/jLJ1VwU+HJuTVN8u7S6Fg==",
+      "dev": true
+    },
     "@babel/code-frame": {
       "version": "7.5.5",
       "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.5.5.tgz",
@@ -37,27 +65,134 @@
       }
     },
     "@babel/core": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.7.4.tgz",
-      "integrity": "sha512-+bYbx56j4nYBmpsWtnPUsKW3NdnYxbqyfrP2w9wILBuHzdfIKz9prieZK0DFPyIzkjYVUe4QkusGL07r5pXznQ==",
-      "dev": true,
-      "requires": {
-        "@babel/code-frame": "^7.5.5",
-        "@babel/generator": "^7.7.4",
-        "@babel/helpers": "^7.7.4",
-        "@babel/parser": "^7.7.4",
-        "@babel/template": "^7.7.4",
-        "@babel/traverse": "^7.7.4",
-        "@babel/types": "^7.7.4",
+      "version": "7.11.1",
+      "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.11.1.tgz",
+      "integrity": "sha512-XqF7F6FWQdKGGWAzGELL+aCO1p+lRY5Tj5/tbT3St1G8NaH70jhhDIKknIZaDans0OQBG5wRAldROLHSt44BgQ==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.10.4",
+        "@babel/generator": "^7.11.0",
+        "@babel/helper-module-transforms": "^7.11.0",
+        "@babel/helpers": "^7.10.4",
+        "@babel/parser": "^7.11.1",
+        "@babel/template": "^7.10.4",
+        "@babel/traverse": "^7.11.0",
+        "@babel/types": "^7.11.0",
         "convert-source-map": "^1.7.0",
         "debug": "^4.1.0",
-        "json5": "^2.1.0",
-        "lodash": "^4.17.13",
+        "gensync": "^1.0.0-beta.1",
+        "json5": "^2.1.2",
+        "lodash": "^4.17.19",
         "resolve": "^1.3.2",
         "semver": "^5.4.1",
         "source-map": "^0.5.0"
       },
       "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+          "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "@babel/generator": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
+          "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0",
+            "jsesc": "^2.5.1",
+            "source-map": "^0.5.0"
+          }
+        },
+        "@babel/helper-function-name": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+          "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-get-function-arity": "^7.10.4",
+            "@babel/template": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-get-function-arity": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+          "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-split-export-declaration": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+          "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+          "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.11.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
+          "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+          "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/traverse": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
+          "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/generator": "^7.11.0",
+            "@babel/helper-function-name": "^7.10.4",
+            "@babel/helper-split-export-declaration": "^7.11.0",
+            "@babel/parser": "^7.11.0",
+            "@babel/types": "^7.11.0",
+            "debug": "^4.1.0",
+            "globals": "^11.1.0",
+            "lodash": "^4.17.19"
+          }
+        },
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        },
         "semver": {
           "version": "5.7.1",
           "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.1.tgz",
@@ -98,12 +233,331 @@
         "@babel/types": "^7.7.4"
       }
     },
+    "@babel/helper-member-expression-to-functions": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-member-expression-to-functions/-/helper-member-expression-to-functions-7.11.0.tgz",
+      "integrity": "sha512-JbFlKHFntRV5qKw3YC0CvQnDZ4XMwgzzBbld7Ly4Mj4cbFy3KywcR8NtNctRToMWJOVvLINJv525Gd6wwVEx/Q==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.11.0"
+      },
+      "dependencies": {
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
+    "@babel/helper-module-imports": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.10.4.tgz",
+      "integrity": "sha512-nEQJHqYavI217oD9+s5MUBzk6x1IlvoS9WTPfgG43CbMEeStE0v+r+TucWdx8KFGowPGvyOkDT9+7DHedIDnVw==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      },
+      "dependencies": {
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
+    "@babel/helper-module-transforms": {
+      "version": "7.11.0",
+      "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.11.0.tgz",
+      "integrity": "sha512-02EVu8COMuTRO1TAzdMtpBPbe6aQ1w/8fePD2YgQmxZU4gpNWaL9gK3Jp7dxlkUlUCJOTaSeA+Hrm1BRQwqIhg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-module-imports": "^7.10.4",
+        "@babel/helper-replace-supers": "^7.10.4",
+        "@babel/helper-simple-access": "^7.10.4",
+        "@babel/helper-split-export-declaration": "^7.11.0",
+        "@babel/template": "^7.10.4",
+        "@babel/types": "^7.11.0",
+        "lodash": "^4.17.19"
+      },
+      "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+          "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "@babel/helper-split-export-declaration": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+          "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+          "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.11.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
+          "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+          "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
+    "@babel/helper-optimise-call-expression": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-optimise-call-expression/-/helper-optimise-call-expression-7.10.4.tgz",
+      "integrity": "sha512-n3UGKY4VXwXThEiKrgRAoVPBMqeoPgHVqiHZOanAJCG9nQUL2pLRQirUzl0ioKclHGpGqRgIOkgcIJaIWLpygg==",
+      "dev": true,
+      "requires": {
+        "@babel/types": "^7.10.4"
+      },
+      "dependencies": {
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
     "@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.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.10.4.tgz",
+      "integrity": "sha512-O4KCvQA6lLiMU9l2eawBPMf1xPP8xPfB3iEQw150hOVTqj/rfXz0ThTb4HEzqQfs2Bmo5Ay8BzxfzVtBrr9dVg==",
       "dev": true
     },
+    "@babel/helper-replace-supers": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-replace-supers/-/helper-replace-supers-7.10.4.tgz",
+      "integrity": "sha512-sPxZfFXocEymYTdVK1UNmFPBN+Hv5mJkLPsYWwGBxZAxaWfFu+xqp7b6qWD0yjNuNL2VKc6L5M18tOXUP7NU0A==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-member-expression-to-functions": "^7.10.4",
+        "@babel/helper-optimise-call-expression": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      },
+      "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+          "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "@babel/generator": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
+          "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0",
+            "jsesc": "^2.5.1",
+            "source-map": "^0.5.0"
+          }
+        },
+        "@babel/helper-function-name": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+          "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-get-function-arity": "^7.10.4",
+            "@babel/template": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-get-function-arity": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+          "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-split-export-declaration": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+          "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+          "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.11.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
+          "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+          "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/traverse": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
+          "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/generator": "^7.11.0",
+            "@babel/helper-function-name": "^7.10.4",
+            "@babel/helper-split-export-declaration": "^7.11.0",
+            "@babel/parser": "^7.11.0",
+            "@babel/types": "^7.11.0",
+            "debug": "^4.1.0",
+            "globals": "^11.1.0",
+            "lodash": "^4.17.19"
+          }
+        },
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
+    "@babel/helper-simple-access": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-simple-access/-/helper-simple-access-7.10.4.tgz",
+      "integrity": "sha512-0fMy72ej/VEvF8ULmX6yb5MtHG4uH4Dbd6I/aHDb/JVg0bbivwt9Wg+h3uMvX+QSFtwr5MeItvazbrc4jtRAXw==",
+      "dev": true,
+      "requires": {
+        "@babel/template": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      },
+      "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+          "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+          "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.11.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
+          "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+          "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
+      }
+    },
     "@babel/helper-split-export-declaration": {
       "version": "7.7.4",
       "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.7.4.tgz",
@@ -113,15 +567,128 @@
         "@babel/types": "^7.7.4"
       }
     },
+    "@babel/helper-validator-identifier": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.10.4.tgz",
+      "integrity": "sha512-3U9y+43hz7ZM+rzG24Qe2mufW5KhvFg/NhnNph+i9mgCtdTCtMJuI1TMkrIUiK7Ix4PYlRF9I5dhqaLYA/ADXw==",
+      "dev": true
+    },
     "@babel/helpers": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.7.4.tgz",
-      "integrity": "sha512-ak5NGZGJ6LV85Q1Zc9gn2n+ayXOizryhjSUBTdu5ih1tlVCJeuQENzc4ItyCVhINVXvIT/ZQ4mheGIsfBkpskg==",
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.10.4.tgz",
+      "integrity": "sha512-L2gX/XeUONeEbI78dXSrJzGdz4GQ+ZTA/aazfUsFaWjSe95kiCuOZ5HsXvkiw3iwF+mFHSRUfJU8t6YavocdXA==",
       "dev": true,
       "requires": {
-        "@babel/template": "^7.7.4",
-        "@babel/traverse": "^7.7.4",
-        "@babel/types": "^7.7.4"
+        "@babel/template": "^7.10.4",
+        "@babel/traverse": "^7.10.4",
+        "@babel/types": "^7.10.4"
+      },
+      "dependencies": {
+        "@babel/code-frame": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.10.4.tgz",
+          "integrity": "sha512-vG6SvB6oYEhvgisZNFRmRCUkLz11c7rp+tbNTynGqc6mS1d5ATd/sGyV6W0KZZnXRKMTzZDRgQT3Ou9jhpAfUg==",
+          "dev": true,
+          "requires": {
+            "@babel/highlight": "^7.10.4"
+          }
+        },
+        "@babel/generator": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.11.0.tgz",
+          "integrity": "sha512-fEm3Uzw7Mc9Xi//qU20cBKatTfs2aOtKqmvy/Vm7RkJEGFQ4xc9myCfbXxqK//ZS8MR/ciOHw6meGASJuKmDfQ==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0",
+            "jsesc": "^2.5.1",
+            "source-map": "^0.5.0"
+          }
+        },
+        "@babel/helper-function-name": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.10.4.tgz",
+          "integrity": "sha512-YdaSyz1n8gY44EmN7x44zBn9zQ1Ry2Y+3GTA+3vH6Mizke1Vw0aWDM66FOYEPw8//qKkmqOckrGgTYa+6sceqQ==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-get-function-arity": "^7.10.4",
+            "@babel/template": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-get-function-arity": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.10.4.tgz",
+          "integrity": "sha512-EkN3YDB+SRDgiIUnNgcmiD361ti+AVbL3f3Henf6dqqUyr5dMsorno0lJWJuLhDhkI5sYEpgj6y9kB8AOU1I2A==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/helper-split-export-declaration": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.11.0.tgz",
+          "integrity": "sha512-74Vejvp6mHkGE+m+k5vHY93FX2cAtrw1zXrZXRlG4l410Nm9PxfEiVTn1PjDPV5SnmieiueY4AFg2xqhNFuuZg==",
+          "dev": true,
+          "requires": {
+            "@babel/types": "^7.11.0"
+          }
+        },
+        "@babel/highlight": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.10.4.tgz",
+          "integrity": "sha512-i6rgnR/YgPEQzZZnbTHHuZdlE8qyoBNalD6F+q4vAFlcMEcqmkoG+mPqJYJCo63qPf74+Y1UZsl3l6f7/RIkmA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "chalk": "^2.0.0",
+            "js-tokens": "^4.0.0"
+          }
+        },
+        "@babel/parser": {
+          "version": "7.11.3",
+          "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.11.3.tgz",
+          "integrity": "sha512-REo8xv7+sDxkKvoxEywIdsNFiZLybwdI7hcT5uEPyQrSMB4YQ973BfC9OOrD/81MaIjh6UxdulIQXkjmiH3PcA==",
+          "dev": true
+        },
+        "@babel/template": {
+          "version": "7.10.4",
+          "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.10.4.tgz",
+          "integrity": "sha512-ZCjD27cGJFUB6nmCB1Enki3r+L5kJveX9pq1SvAUKoICy6CZ9yD8xO086YXdYhvNjBdnekm4ZnaP5yC8Cs/1tA==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/parser": "^7.10.4",
+            "@babel/types": "^7.10.4"
+          }
+        },
+        "@babel/traverse": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.11.0.tgz",
+          "integrity": "sha512-ZB2V+LskoWKNpMq6E5UUCrjtDUh5IOTAyIl0dTjIEoXum/iKWkoIEKIRDnUucO6f+2FzNkE0oD4RLKoPIufDtg==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.10.4",
+            "@babel/generator": "^7.11.0",
+            "@babel/helper-function-name": "^7.10.4",
+            "@babel/helper-split-export-declaration": "^7.11.0",
+            "@babel/parser": "^7.11.0",
+            "@babel/types": "^7.11.0",
+            "debug": "^4.1.0",
+            "globals": "^11.1.0",
+            "lodash": "^4.17.19"
+          }
+        },
+        "@babel/types": {
+          "version": "7.11.0",
+          "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.11.0.tgz",
+          "integrity": "sha512-O53yME4ZZI0jO1EVGtF1ePGl0LHirG4P1ibcD80XyzZcKhcMFeCXmh4Xb1ifGBIV233Qg12x4rBfQgA+tmOukA==",
+          "dev": true,
+          "requires": {
+            "@babel/helper-validator-identifier": "^7.10.4",
+            "lodash": "^4.17.19",
+            "to-fast-properties": "^2.0.0"
+          }
+        }
       }
     },
     "@babel/highlight": {
@@ -141,49 +708,139 @@
       "integrity": "sha512-jIwvLO0zCL+O/LmEJQjWA75MQTWwx3c3u2JOTDK5D3/9egrWRRA0/0hk9XXywYnXZVVpzrBYeIQTmhwUaePI9g==",
       "dev": true
     },
-    "@babel/plugin-syntax-object-rest-spread": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.7.4.tgz",
-      "integrity": "sha512-mObR+r+KZq0XhRVS2BrBKBpr5jqrqzlPvS9C9vuOf5ilSwzloAl7RPWLrgKdWS6IreaVrjHxTjtyqFiOisaCwg==",
+    "@babel/plugin-syntax-async-generators": {
+      "version": "7.8.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz",
+      "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==",
       "dev": true,
       "requires": {
-        "@babel/helper-plugin-utils": "^7.0.0"
+        "@babel/helper-plugin-utils": "^7.8.0"
       }
     },
-    "@babel/runtime": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
-      "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
+    "@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": {
-        "regenerator-runtime": "^0.13.2"
+        "@babel/helper-plugin-utils": "^7.8.0"
       }
     },
-    "@babel/runtime-corejs3": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.7.4.tgz",
-      "integrity": "sha512-BBIEhzk8McXDcB3IbOi8zQPzzINUp4zcLesVlBSOcyGhzPUU8Xezk5GAG7Sy5GVhGmAO0zGd2qRSeY2g4Obqxw==",
+    "@babel/plugin-syntax-class-properties": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.10.4.tgz",
+      "integrity": "sha512-GCSBF7iUle6rNugfURwNmCGG3Z/2+opxAMLs1nND4bhEG5PuxTIggDBoeYYSujAlLtsupzOHYJQgPS3pivwXIA==",
       "dev": true,
       "requires": {
-        "core-js-pure": "^3.0.0",
-        "regenerator-runtime": "^0.13.2"
+        "@babel/helper-plugin-utils": "^7.10.4"
       }
     },
-    "@babel/template": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz",
-      "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==",
+    "@babel/plugin-syntax-import-meta": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz",
+      "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==",
       "dev": true,
       "requires": {
-        "@babel/code-frame": "^7.0.0",
-        "@babel/parser": "^7.7.4",
-        "@babel/types": "^7.7.4"
+        "@babel/helper-plugin-utils": "^7.10.4"
       }
     },
-    "@babel/traverse": {
-      "version": "7.7.4",
-      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz",
-      "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==",
+    "@babel/plugin-syntax-json-strings": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz",
+      "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-logical-assignment-operators": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz",
+      "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-nullish-coalescing-operator": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz",
+      "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-numeric-separator": {
+      "version": "7.10.4",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz",
+      "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.10.4"
+      }
+    },
+    "@babel/plugin-syntax-object-rest-spread": {
+      "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.8.0"
+      }
+    },
+    "@babel/plugin-syntax-optional-catch-binding": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz",
+      "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/plugin-syntax-optional-chaining": {
+      "version": "7.8.3",
+      "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz",
+      "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==",
+      "dev": true,
+      "requires": {
+        "@babel/helper-plugin-utils": "^7.8.0"
+      }
+    },
+    "@babel/runtime": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.7.4.tgz",
+      "integrity": "sha512-r24eVUUr0QqNZa+qrImUk8fn5SPhHq+IfYvIoIMg0do3GdK9sMdiLKP3GYVVaxpPKORgm8KRKaNTEhAjgIpLMw==",
+      "dev": true,
+      "requires": {
+        "regenerator-runtime": "^0.13.2"
+      }
+    },
+    "@babel/runtime-corejs3": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/@babel/runtime-corejs3/-/runtime-corejs3-7.7.4.tgz",
+      "integrity": "sha512-BBIEhzk8McXDcB3IbOi8zQPzzINUp4zcLesVlBSOcyGhzPUU8Xezk5GAG7Sy5GVhGmAO0zGd2qRSeY2g4Obqxw==",
+      "dev": true,
+      "requires": {
+        "core-js-pure": "^3.0.0",
+        "regenerator-runtime": "^0.13.2"
+      }
+    },
+    "@babel/template": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.7.4.tgz",
+      "integrity": "sha512-qUzihgVPguAzXCK7WXw8pqs6cEwi54s3E+HrejlkuWO6ivMKx9hZl3Y2fSXp9i5HgyWmj7RKP+ulaYnKM4yYxw==",
+      "dev": true,
+      "requires": {
+        "@babel/code-frame": "^7.0.0",
+        "@babel/parser": "^7.7.4",
+        "@babel/types": "^7.7.4"
+      }
+    },
+    "@babel/traverse": {
+      "version": "7.7.4",
+      "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.7.4.tgz",
+      "integrity": "sha512-P1L58hQyupn8+ezVA2z5KBm4/Zr4lCC8dwKCMYzsa5jFMDMQAzaBNy9W5VjB+KAmBjb40U7a/H6ao+Xo+9saIw==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.5.5",
@@ -208,537 +865,1582 @@
         "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"
+      }
+    },
+    "@graphql-tools/delegate": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/delegate/-/delegate-6.0.16.tgz",
+      "integrity": "sha512-mq/vTHaBGOWxqKqjkj8KJpH+hg6Y096nZYTLpUZcPf6eX1OhxEIkdw5NDN99ii2/NGAyw7ApoY7BWFEEneiiTg==",
+      "dev": true,
+      "requires": {
+        "@ardatan/aggregate-error": "0.0.1",
+        "@graphql-tools/schema": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "tslib": "~2.0.0"
       },
       "dependencies": {
-        "minimist": {
-          "version": "1.2.0",
-          "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz",
-          "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=",
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
           "dev": true
         }
       }
     },
-    "@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==",
+    "@graphql-tools/graphql-file-loader": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/graphql-file-loader/-/graphql-file-loader-6.0.16.tgz",
+      "integrity": "sha512-qgYplQhnY90CnQiRZpM2svCzyZ7FAXaca+liZ6hqA9jfWUWh4N9Tnmy//BqrTmULGVeanPM/m8MyhZEWvvRNIg==",
       "dev": true,
       "requires": {
-        "@jest/source-map": "^24.9.0",
-        "chalk": "^2.0.1",
-        "slash": "^2.0.0"
+        "@graphql-tools/import": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "fs-extra": "9.0.1",
+        "tslib": "~2.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+          "dev": true
+        }
       }
     },
-    "@jest/core": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/@jest/core/-/core-24.9.0.tgz",
-      "integrity": "sha512-Fogg3s4wlAr1VX7q+rhV9RVnUv5tD7VuWfYy1+whMiWUrvl7U3QJSJyWcDio9Lq2prqYsZaeTv2Rz24pWGkJ2A==",
+    "@graphql-tools/import": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/import/-/import-6.0.16.tgz",
+      "integrity": "sha512-zZRxJwAtUsyIckjfiscteFwpaIuEh3EjuhXEaNviMuwhOSrYT0oWmelcPgp/VHT6N4NZD1/y5jxQ4KHK4MrAfg==",
       "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",
-        "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",
-        "realpath-native": "^1.1.0",
-        "rimraf": "^2.5.4",
-        "slash": "^2.0.0",
-        "strip-ansi": "^5.0.0"
+        "fs-extra": "9.0.1",
+        "resolve-from": "5.0.0"
       },
       "dependencies": {
-        "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"
-          }
+        "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
         }
       }
     },
-    "@jest/environment": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-24.9.0.tgz",
-      "integrity": "sha512-5A1QluTPhvdIPFYnO3sZC3smkNeXPVELz7ikPbhUj0bQjB07EoE9qtLrem14ZUYWdVayYbsjVwIiL4WBIMV4aQ==",
+    "@graphql-tools/json-file-loader": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/json-file-loader/-/json-file-loader-6.0.16.tgz",
+      "integrity": "sha512-djkzPmGvVpD3YRypibYRNTgVUUfkae0JXcEWP/gn/8Y8+mnwyE2EiBfGZoW6Ejw5xTKQ7PgmOyWUIJgdEVMCJg==",
       "dev": true,
       "requires": {
-        "@jest/fake-timers": "^24.9.0",
-        "@jest/transform": "^24.9.0",
-        "@jest/types": "^24.9.0",
-        "jest-mock": "^24.9.0"
+        "@graphql-tools/utils": "6.0.16",
+        "fs-extra": "9.0.1",
+        "tslib": "~2.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+          "dev": true
+        }
       }
     },
-    "@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==",
+    "@graphql-tools/load": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/load/-/load-6.0.16.tgz",
+      "integrity": "sha512-7nJUrQqou8lQG5x6tJQAl0N/ONP2oYEgSmN0QwjSxv8iz0aRDoK/nHzGlVk6/Sot58iogF0E+qx/vDKNJh2piw==",
       "dev": true,
       "requires": {
-        "@jest/types": "^24.9.0",
-        "jest-message-util": "^24.9.0",
-        "jest-mock": "^24.9.0"
+        "@graphql-tools/merge": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "globby": "11.0.1",
+        "import-from": "3.0.0",
+        "is-glob": "4.0.1",
+        "p-limit": "3.0.2",
+        "tslib": "~2.0.0",
+        "unixify": "1.0.0",
+        "valid-url": "1.0.9"
+      },
+      "dependencies": {
+        "p-limit": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.0.2.tgz",
+          "integrity": "sha512-iwqZSOoWIW+Ew4kAGUlN16J4M7OB3ysMLSZtnhmqx7njIHFPlxWBX8xo3lVTyFVq6mI/lL9qt2IsN1sHwaxJkg==",
+          "dev": true,
+          "requires": {
+            "p-try": "^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
+        },
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+          "dev": true
+        }
       }
     },
-    "@jest/reporters": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-24.9.0.tgz",
-      "integrity": "sha512-mu4X0yjaHrffOsWmVLzitKmmmWSQ3GGuefgNscUSWNiUNcEOSEQk9k3pERKEQVBb0Cnn88+UESIsZEMH3o88Gw==",
+    "@graphql-tools/merge": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/merge/-/merge-6.0.16.tgz",
+      "integrity": "sha512-QWeTru5IAON9ruTqs48X3WndRjz4pamTfA90M/RICkgog1LsFbIFhHM2QF+hogoMqxhlhmjgfMjQl7xXtDT+9Q==",
       "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",
-        "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",
-        "source-map": "^0.6.0",
-        "string-length": "^2.0.0"
+        "@graphql-tools/schema": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "tslib": "~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==",
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
           "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==",
+    "@graphql-tools/schema": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/schema/-/schema-6.0.16.tgz",
+      "integrity": "sha512-e5jqE13L5eywCc0Uqlf2ThgScj1KgrCQmwvm+giVK0Dh9goMbwLZt/ciEJSr/LYn/vsH5sec9Qu5Jml6IX7zLA==",
       "dev": true,
       "requires": {
-        "callsites": "^3.0.0",
-        "graceful-fs": "^4.1.15",
-        "source-map": "^0.6.0"
+        "@graphql-tools/utils": "6.0.16",
+        "tslib": "~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==",
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
           "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==",
+    "@graphql-tools/url-loader": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/url-loader/-/url-loader-6.0.16.tgz",
+      "integrity": "sha512-b+dwCDTcWIGOCYNYUKr6nbkAi8uOmgYHCf1wXsG09gV8uchU74tL8ebxBoaIEU8C9GSqterK2Y7mNjWyw3UdQQ==",
       "dev": true,
       "requires": {
-        "@jest/console": "^24.9.0",
-        "@jest/types": "^24.9.0",
-        "@types/istanbul-lib-coverage": "^2.0.0"
+        "@graphql-tools/delegate": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "@graphql-tools/wrap": "6.0.16",
+        "@types/websocket": "1.0.1",
+        "cross-fetch": "3.0.5",
+        "subscriptions-transport-ws": "0.9.17",
+        "tslib": "~2.0.0",
+        "valid-url": "1.0.9",
+        "websocket": "1.0.31"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+          "dev": true
+        }
       }
     },
-    "@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==",
+    "@graphql-tools/utils": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/utils/-/utils-6.0.16.tgz",
+      "integrity": "sha512-WSYVqiIpda0CzXgHuKBJkqE0zZs4aruoVxn5KVMmqDoZbPVJ4f/pATVgKYyelOlBlx5gOfs8PCFpWcQhDB39LA==",
       "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"
+        "@ardatan/aggregate-error": "0.0.1",
+        "camel-case": "4.1.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==",
+    "@graphql-tools/wrap": {
+      "version": "6.0.16",
+      "resolved": "https://registry.npmjs.org/@graphql-tools/wrap/-/wrap-6.0.16.tgz",
+      "integrity": "sha512-Q1VECNmmRygX1qdlGEF6dimAiuX9rauqalJif2zL9Fa4uORSvPr3VxOA8A0+4ypz2QYL+PjqQ88rCATUZxpY9g==",
       "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"
+        "@graphql-tools/delegate": "6.0.16",
+        "@graphql-tools/schema": "6.0.16",
+        "@graphql-tools/utils": "6.0.16",
+        "aggregate-error": "3.0.1",
+        "tslib": "~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==",
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
           "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==",
+    "@istanbuljs/load-nyc-config": {
+      "version": "1.1.0",
+      "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz",
+      "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==",
       "dev": true,
       "requires": {
-        "@types/istanbul-lib-coverage": "^2.0.0",
-        "@types/istanbul-reports": "^1.1.1",
-        "@types/yargs": "^13.0.0"
-      }
-    },
-    "@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.2",
-      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.2.tgz",
-      "integrity": "sha512-ICDcRA0C2vtTZZGud1nXRrBLXZqFayodXAKZfo3dkdcLNqcHsgaz3YSTupbURusYeucSVRjjG+RTcQhx6HPPcg==",
-      "requires": {
-        "@octokit/types": "^2.0.0",
-        "is-plain-object": "^3.0.0",
-        "universal-user-agent": "^4.0.0"
+        "camelcase": "^5.3.1",
+        "find-up": "^4.1.0",
+        "get-package-type": "^0.1.0",
+        "js-yaml": "^3.13.1",
+        "resolve-from": "^5.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==",
+        "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": {
-            "isobject": "^4.0.0"
+            "locate-path": "^5.0.0",
+            "path-exists": "^4.0.0"
           }
         },
-        "isobject": {
+        "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.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "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/isobject/-/isobject-4.0.0.tgz",
-          "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
+          "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
         }
       }
     },
-    "@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==",
-      "requires": {
-        "@octokit/request": "^5.3.0",
-        "@octokit/types": "^2.0.0",
-        "universal-user-agent": "^4.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
     },
-    "@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==",
+    "@jest/console": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/@jest/console/-/console-26.2.0.tgz",
+      "integrity": "sha512-mXQfx3nSLwiHm1i7jbu+uvi+vvpVjNGzIQYLCfsat9rapC+MJkS4zBseNrgJE0vU921b3P67bQzhduphjY3Tig==",
+      "dev": true,
       "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",
-      "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==",
-      "requires": {
-        "@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": "^4.0.0"
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "jest-message-util": "^26.2.0",
+        "jest-util": "^26.2.0",
+        "slash": "^3.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==",
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
           "requires": {
-            "isobject": "^4.0.0"
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
           }
         },
-        "isobject": {
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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/isobject/-/isobject-4.0.0.tgz",
-          "integrity": "sha512-S/2fF5wH8SJA/kmwr6HYhK/RI/OkhD84k8ntalo0iJjZikgq1XFvR5M8NPT1x5F7fBwCG3qHfnzeP/Vh/ZxCUA=="
+          "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz",
+          "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==",
+          "dev": true
         },
-        "node-fetch": {
-          "version": "2.6.0",
-          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
-          "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+        "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"
+          }
         }
       }
     },
-    "@octokit/request-error": {
-      "version": "1.2.1",
-      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
-      "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
-      "requires": {
-        "@octokit/types": "^2.0.0",
-        "deprecation": "^2.0.0",
-        "once": "^1.4.0"
-      }
-    },
-    "@octokit/rest": {
-      "version": "16.43.0",
-      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.0.tgz",
-      "integrity": "sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==",
-      "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.1.1",
-      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz",
-      "integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==",
-      "requires": {
-        "@types/node": ">= 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==",
-      "dev": true,
-      "requires": {
-        "@babel/parser": "^7.1.0",
-        "@babel/types": "^7.0.0",
-        "@types/babel__generator": "*",
-        "@types/babel__template": "*",
-        "@types/babel__traverse": "*"
-      }
-    },
-    "@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==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "@types/babel__template": {
-      "version": "7.0.2",
-      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz",
-      "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==",
-      "dev": true,
-      "requires": {
-        "@babel/parser": "^7.1.0",
-        "@babel/types": "^7.0.0"
-      }
-    },
-    "@types/babel__traverse": {
-      "version": "7.0.8",
-      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.8.tgz",
-      "integrity": "sha512-yGeB2dHEdvxjP0y4UbRtQaSkXJ9649fYCmIdRoul5kfAoGCwxuCbMhag0k3RPfnuh9kPGm8x89btcfDEXdVWGw==",
-      "dev": true,
-      "requires": {
-        "@babel/types": "^7.3.0"
-      }
-    },
-    "@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/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
-    },
-    "@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.23",
-      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.23.tgz",
-      "integrity": "sha512-L7MBvwfNpe7yVPTXLn32df/EK+AMBFAFvZrRuArGs7npEWnlziUXK+5GMIUTI4NIuwok3XibsjXCs5HxviYXjg==",
-      "dev": true,
-      "requires": {
-        "jest-diff": "^24.3.0"
-      }
-    },
-    "@types/json-schema": {
-      "version": "7.0.3",
-      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
-      "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
-      "dev": true
-    },
-    "@types/node": {
-      "version": "12.12.14",
-      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz",
-      "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA=="
-    },
-    "@types/stack-utils": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
-      "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==",
-      "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==",
-      "dev": true,
-      "requires": {
-        "@types/yargs-parser": "*"
-      }
-    },
-    "@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.13.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
-      "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
-      "dev": true,
-      "requires": {
-        "@typescript-eslint/experimental-utils": "1.13.0",
-        "eslint-utils": "^1.3.1",
-        "functional-red-black-tree": "^1.0.1",
-        "regexpp": "^2.0.1",
-        "tsutils": "^3.7.0"
+    "@jest/core": {
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/@jest/core/-/core-26.2.2.tgz",
+      "integrity": "sha512-UwA8gNI8aeV4FHGfGAUfO/DHjrFVvlBravF1Tm9Kt6qFE+6YHR47kFhgdepOFpADEKstyO+MVdPvkV6/dyt9sA==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^26.2.0",
+        "@jest/reporters": "^26.2.2",
+        "@jest/test-result": "^26.2.0",
+        "@jest/transform": "^26.2.2",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "ansi-escapes": "^4.2.1",
+        "chalk": "^4.0.0",
+        "exit": "^0.1.2",
+        "graceful-fs": "^4.2.4",
+        "jest-changed-files": "^26.2.0",
+        "jest-config": "^26.2.2",
+        "jest-haste-map": "^26.2.2",
+        "jest-message-util": "^26.2.0",
+        "jest-regex-util": "^26.0.0",
+        "jest-resolve": "^26.2.2",
+        "jest-resolve-dependencies": "^26.2.2",
+        "jest-runner": "^26.2.2",
+        "jest-runtime": "^26.2.2",
+        "jest-snapshot": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "jest-validate": "^26.2.0",
+        "jest-watcher": "^26.2.0",
+        "micromatch": "^4.0.2",
+        "p-each-series": "^2.1.0",
+        "rimraf": "^3.0.0",
+        "slash": "^3.0.0",
+        "strip-ansi": "^6.0.0"
       },
       "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==",
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
           "dev": true,
           "requires": {
-            "@types/json-schema": "^7.0.3",
-            "@typescript-eslint/typescript-estree": "1.13.0",
-            "eslint-scope": "^4.0.0"
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.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==",
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
           "dev": true,
           "requires": {
-            "lodash.unescape": "4.0.1",
-            "semver": "5.5.0"
+            "@types/yargs-parser": "*"
           }
         },
-        "eslint-scope": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+        "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": {
-            "esrecurse": "^4.1.0",
-            "estraverse": "^4.1.1"
+            "type-fest": "^0.11.0"
           }
         },
-        "semver": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+        "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
-        }
-      }
-    },
-    "@typescript-eslint/experimental-utils": {
-      "version": "2.10.0",
-      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz",
-      "integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==",
+        },
+        "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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"
+          }
+        },
+        "strip-ansi": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz",
+          "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^5.0.0"
+          }
+        },
+        "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"
+          }
+        },
+        "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
+        }
+      }
+    },
+    "@jest/environment": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-26.2.0.tgz",
+      "integrity": "sha512-oCgp9NmEiJ5rbq9VI/v/yYLDpladAAVvFxZgNsnJxOETuzPZ0ZcKKHYjKYwCtPOP1WCrM5nmyuOhMStXFGHn+g==",
+      "dev": true,
+      "requires": {
+        "@jest/fake-timers": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "jest-mock": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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/fake-timers": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-26.2.0.tgz",
+      "integrity": "sha512-45Gfe7YzYTKqTayBrEdAF0qYyAsNRBzfkV0IyVUm3cx7AsCWlnjilBM4T40w7IXT5VspOgMPikQlV0M6gHwy/g==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^26.2.0",
+        "@sinonjs/fake-timers": "^6.0.1",
+        "@types/node": "*",
+        "jest-message-util": "^26.2.0",
+        "jest-mock": "^26.2.0",
+        "jest-util": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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/globals": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-26.2.0.tgz",
+      "integrity": "sha512-Hoc6ScEIPaym7RNytIL2ILSUWIGKlwEv+JNFof9dGYOdvPjb2evEURSslvCMkNuNg1ECEClTE8PH7ULlMJntYA==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "expect": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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/reporters": {
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-26.2.2.tgz",
+      "integrity": "sha512-7854GPbdFTAorWVh+RNHyPO9waRIN6TcvCezKVxI1khvFq9YjINTW7J3WU+tbR038Ynn6WjYred6vtT0YmIWVQ==",
+      "dev": true,
+      "requires": {
+        "@bcoe/v8-coverage": "^0.2.3",
+        "@jest/console": "^26.2.0",
+        "@jest/test-result": "^26.2.0",
+        "@jest/transform": "^26.2.2",
+        "@jest/types": "^26.2.0",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
+        "exit": "^0.1.2",
+        "glob": "^7.1.2",
+        "graceful-fs": "^4.2.4",
+        "istanbul-lib-coverage": "^3.0.0",
+        "istanbul-lib-instrument": "^4.0.3",
+        "istanbul-lib-report": "^3.0.0",
+        "istanbul-lib-source-maps": "^4.0.0",
+        "istanbul-reports": "^3.0.2",
+        "jest-haste-map": "^26.2.2",
+        "jest-resolve": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "jest-worker": "^26.2.1",
+        "node-notifier": "^7.0.0",
+        "slash": "^3.0.0",
+        "source-map": "^0.6.0",
+        "string-length": "^4.0.1",
+        "terminal-link": "^2.0.0",
+        "v8-to-istanbul": "^4.1.3"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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"
+          }
+        }
+      }
+    },
+    "@jest/source-map": {
+      "version": "26.1.0",
+      "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-26.1.0.tgz",
+      "integrity": "sha512-XYRPYx4eEVX15cMT9mstnO7hkHP3krNtKfxUYd8L7gbtia8JvZZ6bMzSwa6IQJENbudTwKMw5R1BePRD+bkEmA==",
+      "dev": true,
+      "requires": {
+        "callsites": "^3.0.0",
+        "graceful-fs": "^4.2.4",
+        "source-map": "^0.6.0"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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/test-result": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-26.2.0.tgz",
+      "integrity": "sha512-kgPlmcVafpmfyQEu36HClK+CWI6wIaAWDHNxfQtGuKsgoa2uQAYdlxjMDBEa3CvI40+2U3v36gQF6oZBkoKatw==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/istanbul-lib-coverage": "^2.0.0",
+        "collect-v8-coverage": "^1.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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/test-sequencer": {
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-26.2.2.tgz",
+      "integrity": "sha512-SliZWon5LNqV/lVXkeowSU6L8++FGOu3f43T01L1Gv6wnFDP00ER0utV9jyK9dVNdXqfMNCN66sfcyar/o7BNw==",
+      "dev": true,
+      "requires": {
+        "@jest/test-result": "^26.2.0",
+        "graceful-fs": "^4.2.4",
+        "jest-haste-map": "^26.2.2",
+        "jest-runner": "^26.2.2",
+        "jest-runtime": "^26.2.2"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "dev": true
+        }
+      }
+    },
+    "@jest/transform": {
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-26.2.2.tgz",
+      "integrity": "sha512-c1snhvi5wRVre1XyoO3Eef5SEWpuBCH/cEbntBUd9tI5sNYiBDmO0My/lc5IuuGYKp/HFIHV1eZpSx5yjdkhKw==",
+      "dev": true,
+      "requires": {
+        "@babel/core": "^7.1.0",
+        "@jest/types": "^26.2.0",
+        "babel-plugin-istanbul": "^6.0.0",
+        "chalk": "^4.0.0",
+        "convert-source-map": "^1.4.0",
+        "fast-json-stable-stringify": "^2.0.0",
+        "graceful-fs": "^4.2.4",
+        "jest-haste-map": "^26.2.2",
+        "jest-regex-util": "^26.0.0",
+        "jest-util": "^26.2.0",
+        "micromatch": "^4.0.2",
+        "pirates": "^4.0.1",
+        "slash": "^3.0.0",
+        "source-map": "^0.6.1",
+        "write-file-atomic": "^3.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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"
+          }
+        }
+      }
+    },
+    "@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"
+      }
+    },
+    "@nodelib/fs.scandir": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz",
+      "integrity": "sha512-eGmwYQn3gxo4r7jdQnkrrN6bY478C3P+a/y72IJukF8LjB6ZHeB3c+Ehacj3sYeSmUXGlnA67/PmbM9CVwL7Dw==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "2.0.3",
+        "run-parallel": "^1.1.9"
+      }
+    },
+    "@nodelib/fs.stat": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.3.tgz",
+      "integrity": "sha512-bQBFruR2TAwoevBEd/NWMoAAtNGzTRgdrqnYCc7dhzfoNvqPzLyqlEQnzZ3kVnNrSp25iyxE00/3h2fqGAGArA==",
+      "dev": true
+    },
+    "@nodelib/fs.walk": {
+      "version": "1.2.4",
+      "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.4.tgz",
+      "integrity": "sha512-1V9XOY4rDW0rehzbrcqAmHnz8e7SKvX27gh8Gt2WgB0+pdzdiLV83p72kZPU+jvMbS1qU5mauP2iOvO8rhmurQ==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.scandir": "2.1.3",
+        "fastq": "^1.6.0"
+      }
+    },
+    "@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.2",
+      "resolved": "https://registry.npmjs.org/@octokit/endpoint/-/endpoint-5.5.2.tgz",
+      "integrity": "sha512-ICDcRA0C2vtTZZGud1nXRrBLXZqFayodXAKZfo3dkdcLNqcHsgaz3YSTupbURusYeucSVRjjG+RTcQhx6HPPcg==",
+      "requires": {
+        "@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==",
+          "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=="
+        }
+      }
+    },
+    "@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==",
+      "requires": {
+        "@octokit/request": "^5.3.0",
+        "@octokit/types": "^2.0.0",
+        "universal-user-agent": "^4.0.0"
+      }
+    },
+    "@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",
+      "integrity": "sha512-5/X0AL1ZgoU32fAepTfEoggFinO3rxsMLtzhlUX+RctLrusn/CApJuGFCd0v7GMFhF+8UiCsTTfsu7Fh1HnEJg==",
+      "requires": {
+        "@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": "^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==",
+          "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=="
+        },
+        "node-fetch": {
+          "version": "2.6.0",
+          "resolved": "https://registry.npmjs.org/node-fetch/-/node-fetch-2.6.0.tgz",
+          "integrity": "sha512-8dG4H5ujfvFiqDmVu9fQ5bOHUC15JMjMY/Zumv26oOvvVJjM67KF8koCWIabKQ1GJIa9r2mMZscBq/TbdOcmNA=="
+        }
+      }
+    },
+    "@octokit/request-error": {
+      "version": "1.2.1",
+      "resolved": "https://registry.npmjs.org/@octokit/request-error/-/request-error-1.2.1.tgz",
+      "integrity": "sha512-+6yDyk1EES6WK+l3viRDElw96MvwfJxCt45GvmjDUKWjYIb3PJZQkq3i46TwGwoPD4h8NmTrENmtyA1FwbmhRA==",
+      "requires": {
+        "@octokit/types": "^2.0.0",
+        "deprecation": "^2.0.0",
+        "once": "^1.4.0"
+      }
+    },
+    "@octokit/rest": {
+      "version": "16.43.0",
+      "resolved": "https://registry.npmjs.org/@octokit/rest/-/rest-16.43.0.tgz",
+      "integrity": "sha512-u+OwrTxHuppVcssGmwCmb4jgPNzsRseJ2rS5PrZk2ASC+WkaF5Q7wu8zVtJ4OA24jK6aRymlwA2uwL36NU9nAA==",
+      "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.1.1",
+      "resolved": "https://registry.npmjs.org/@octokit/types/-/types-2.1.1.tgz",
+      "integrity": "sha512-89LOYH+d/vsbDX785NOfLxTW88GjNd0lWRz1DVPVsZgg9Yett5O+3MOvwo7iHgvUwbFz0mf/yPIjBkUbs4kxoQ==",
+      "requires": {
+        "@types/node": ">= 8"
+      }
+    },
+    "@sinonjs/commons": {
+      "version": "1.8.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-1.8.1.tgz",
+      "integrity": "sha512-892K+kWUUi3cl+LlqEWIDrhvLgdL79tECi8JZUyq6IviKy/DNhuzCRlbHUjxK89f4ypPMMaFnFuR9Ie6DoIMsw==",
+      "dev": true,
+      "requires": {
+        "type-detect": "4.0.8"
+      }
+    },
+    "@sinonjs/fake-timers": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-6.0.1.tgz",
+      "integrity": "sha512-MZPUxrmFubI36XS1DI3qmI0YdN1gks62JtFZvxR67ljjSNCeK6U08Zx4msEWOXuofgqUt6zPHSi1H9fbjR/NRA==",
+      "dev": true,
+      "requires": {
+        "@sinonjs/commons": "^1.7.0"
+      }
+    },
+    "@types/babel__core": {
+      "version": "7.1.9",
+      "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.1.9.tgz",
+      "integrity": "sha512-sY2RsIJ5rpER1u3/aQ8OFSI7qGIy8o1NEEbgb2UaJcvOtXOMpd39ko723NBpjQFg9SIX7TXtjejZVGeIMLhoOw==",
+      "dev": true,
+      "requires": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0",
+        "@types/babel__generator": "*",
+        "@types/babel__template": "*",
+        "@types/babel__traverse": "*"
+      }
+    },
+    "@types/babel__generator": {
+      "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"
+      }
+    },
+    "@types/babel__template": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.0.2.tgz",
+      "integrity": "sha512-/K6zCpeW7Imzgab2bLkLEbz0+1JlFSrUMdw7KoIIu+IUdu51GWaBZpd3y1VXGVXzynvGa4DaIaxNZHiON3GXUg==",
+      "dev": true,
+      "requires": {
+        "@babel/parser": "^7.1.0",
+        "@babel/types": "^7.0.0"
+      }
+    },
+    "@types/babel__traverse": {
+      "version": "7.0.13",
+      "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.0.13.tgz",
+      "integrity": "sha512-i+zS7t6/s9cdQvbqKDARrcbrPvtJGlbYsMkazo03nTAK3RX9FNrLllXys22uiTGJapPOTZTQ35nHh4ISph4SLQ==",
+      "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/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/graceful-fs": {
+      "version": "4.1.3",
+      "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.3.tgz",
+      "integrity": "sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@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
+    },
+    "@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.23",
+      "resolved": "https://registry.npmjs.org/@types/jest/-/jest-24.0.23.tgz",
+      "integrity": "sha512-L7MBvwfNpe7yVPTXLn32df/EK+AMBFAFvZrRuArGs7npEWnlziUXK+5GMIUTI4NIuwok3XibsjXCs5HxviYXjg==",
+      "dev": true,
+      "requires": {
+        "jest-diff": "^24.3.0"
+      }
+    },
+    "@types/json-schema": {
+      "version": "7.0.3",
+      "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.3.tgz",
+      "integrity": "sha512-Il2DtDVRGDcqjDtE+rF8iqg1CArehSK84HZJCT7AMITlyXRBpuPhqGLDQMowraqqu1coEaimg4ZOqggt6L6L+A==",
+      "dev": true
+    },
+    "@types/node": {
+      "version": "12.12.14",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-12.12.14.tgz",
+      "integrity": "sha512-u/SJDyXwuihpwjXy7hOOghagLEV1KdAST6syfnOk6QZAMzZuWZqXy5aYYZbh8Jdpd4escVFP0MvftHNDb9pruA=="
+    },
+    "@types/normalize-package-data": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.0.tgz",
+      "integrity": "sha512-f5j5b/Gf71L+dbqxIpQ4Z2WlmI/mPJ0fOkGGmFgtb6sAu97EPczzbS3/tJKxmcYDj55OX6ssqwDAWOHIYDRDGA==",
+      "dev": true
+    },
+    "@types/parse-json": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/@types/parse-json/-/parse-json-4.0.0.tgz",
+      "integrity": "sha512-//oorEZjL6sbPcKUaCdIGlIUeH26mgzimjBB77G6XRgnDl/L5wOnpyBGRe/Mmf5CVW3PwEBE1NjiMZ/ssFh4wA==",
+      "dev": true
+    },
+    "@types/prettier": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.0.2.tgz",
+      "integrity": "sha512-IkVfat549ggtkZUthUzEX49562eGikhSYeVGX97SkMFn+sTZrgRewXjQ4tPKFPCykZHkX1Zfd9OoELGqKU2jJA==",
+      "dev": true
+    },
+    "@types/stack-utils": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-1.0.1.tgz",
+      "integrity": "sha512-l42BggppR6zLmpfU6fq9HEa2oGPEI8yrSPL3GITjfRInppYFahObbIQOQK3UGxEnyQpltZLaPe75046NOZQikw==",
+      "dev": true
+    },
+    "@types/tmp": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.1.0.tgz",
+      "integrity": "sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA=="
+    },
+    "@types/websocket": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/@types/websocket/-/websocket-1.0.1.tgz",
+      "integrity": "sha512-f5WLMpezwVxCLm1xQe/kdPpQIOmL0TXYx2O15VYfYzc7hTIdxiOoOvez+McSIw3b7z/1zGovew9YSL7+h4h7/Q==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
+    "@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": "*"
+      }
+    },
+    "@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.13.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-1.13.0.tgz",
+      "integrity": "sha512-WQHCozMnuNADiqMtsNzp96FNox5sOVpU8Xt4meaT4em8lOG1SrOv92/mUbEHQVh90sldKSfcOc/I0FOb/14G1g==",
+      "dev": true,
+      "requires": {
+        "@typescript-eslint/experimental-utils": "1.13.0",
+        "eslint-utils": "^1.3.1",
+        "functional-red-black-tree": "^1.0.1",
+        "regexpp": "^2.0.1",
+        "tsutils": "^3.7.0"
+      },
+      "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/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-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
+          "dev": true,
+          "requires": {
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
+          }
+        },
+        "semver": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
+          "dev": true
+        }
+      }
+    },
+    "@typescript-eslint/experimental-utils": {
+      "version": "2.10.0",
+      "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-2.10.0.tgz",
+      "integrity": "sha512-FZhWq6hWWZBP76aZ7bkrfzTMP31CCefVIImrwP3giPLcoXocmLTmr92NLZxuIcTL4GTEOE33jQMWy9PwelL+yQ==",
       "dev": true,
       "requires": {
         "@types/json-schema": "^7.0.3",
@@ -780,9 +2482,9 @@
       "dev": true
     },
     "abab": {
-      "version": "2.0.3",
-      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.3.tgz",
-      "integrity": "sha512-tsFzPpcttalNjFBCFMqsKYQcWxxen1pgJR56by//QwvJc4/OUS3kPOOttx2tSIfjsylB0pYu7f5D3K1RCxUnUg==",
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/abab/-/abab-2.0.4.tgz",
+      "integrity": "sha512-Eu9ELJWCz/c1e9gTiCY+FceWxcqzjYEbqMgtndnuSqZSUCOL73TWNK2mHfIj4Cw2E/ongOp+JISVNCmovt2KYQ==",
       "dev": true
     },
     "acorn": {
@@ -792,27 +2494,45 @@
       "dev": true
     },
     "acorn-globals": {
-      "version": "4.3.4",
-      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-4.3.4.tgz",
-      "integrity": "sha512-clfQEh21R+D0leSbUdWf3OcfqyaCSAQ8Ryq00bofSekfr9W8u1jyYZo6ir0xu9Gtcf7BjcHJpnbZH7JOCpP60A==",
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/acorn-globals/-/acorn-globals-6.0.0.tgz",
+      "integrity": "sha512-ZQl7LOWaF5ePqqcX4hLuv/bLXYQNfNWw2c0/yX/TsPRKamzHcTGQnlCjHT3TsmkOUVEPS3crCxiPfdzE/Trlhg==",
       "dev": true,
       "requires": {
-        "acorn": "^6.0.1",
-        "acorn-walk": "^6.0.1"
+        "acorn": "^7.1.1",
+        "acorn-walk": "^7.1.1"
+      },
+      "dependencies": {
+        "acorn": {
+          "version": "7.4.0",
+          "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.0.tgz",
+          "integrity": "sha512-+G7P8jJmCHr+S+cLfQxygbWhXy+8YTVGzAkpEbcLo2mLoL7tij/VG41QSHACSf5QgYRhMZYHuNc6drJaO0Da+w==",
+          "dev": true
+        }
       }
     },
     "acorn-jsx": {
-      "version": "5.1.0",
-      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.1.0.tgz",
-      "integrity": "sha512-tMUqwBWfLFbJbizRmEcWSLw6HnFzfdJs2sOJEOwwtVPMoH/0Ay+E703oZz78VSXZiiDcZrQ5XKjPIUQixhmgVw==",
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.2.0.tgz",
+      "integrity": "sha512-HiUX/+K2YpkpJ+SzBffkM/AQ2YE03S0U1kjTLVpoJdhZMOWy8qvXVN9JdLqv2QsaQ6MPYQIuNmwD8zOiYUofLQ==",
       "dev": true
     },
     "acorn-walk": {
-      "version": "6.2.0",
-      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-6.2.0.tgz",
-      "integrity": "sha512-7evsyfH1cLOCdAzZAd43Cic04yKydNx0cF+7tiA19p1XnLLPU4dpCQOqpjqwokFe//vS0QqfqqjCS2JkiIs0cA==",
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/acorn-walk/-/acorn-walk-7.2.0.tgz",
+      "integrity": "sha512-OPdCF6GsMIP+Az+aWfAAOEt2/+iVDKE7oy6lJ098aoe59oAmK76qV6Gw60SbZ8jHuG2wH058GF4pLFbYamYrVA==",
       "dev": true
     },
+    "aggregate-error": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/aggregate-error/-/aggregate-error-3.0.1.tgz",
+      "integrity": "sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==",
+      "dev": true,
+      "requires": {
+        "clean-stack": "^2.0.0",
+        "indent-string": "^4.0.0"
+      }
+    },
     "ajv": {
       "version": "6.10.2",
       "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.10.2.tgz",
@@ -847,13 +2567,21 @@
       }
     },
     "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"
+      },
+      "dependencies": {
+        "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
+        }
       }
     },
     "argparse": {
@@ -893,12 +2621,6 @@
       "integrity": "sha1-45sJrqne+Gao8gbiiK9jkZuuOcQ=",
       "dev": true
     },
-    "array-equal": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/array-equal/-/array-equal-1.0.0.tgz",
-      "integrity": "sha1-jCpe8kcv2ep0KwTHenUJO6J1fJM=",
-      "dev": true
-    },
     "array-includes": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/array-includes/-/array-includes-3.0.3.tgz",
@@ -909,6 +2631,12 @@
         "es-abstract": "^1.7.0"
       }
     },
+    "array-union": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz",
+      "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==",
+      "dev": true
+    },
     "array-unique": {
       "version": "0.3.2",
       "resolved": "https://registry.npmjs.org/array-unique/-/array-unique-0.3.2.tgz",
@@ -960,6 +2688,12 @@
       "integrity": "sha1-x57Zf380y48robyXkLzDZkdLS3k=",
       "dev": true
     },
+    "at-least-node": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/at-least-node/-/at-least-node-1.0.0.tgz",
+      "integrity": "sha512-+q/t7Ekv1EDY2l6Gda6LLiX14rU9TV20Wa3ofeQmwPFZbOMo9DXrLbOjFaaclkXKWidIaopwAObQDqwWtGUjqg==",
+      "dev": true
+    },
     "atob": {
       "version": "2.1.2",
       "resolved": "https://registry.npmjs.org/atob/-/atob-2.1.2.tgz",
@@ -978,9 +2712,9 @@
       "dev": true
     },
     "aws4": {
-      "version": "1.9.0",
-      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.9.0.tgz",
-      "integrity": "sha512-Uvq6hVe90D0B2WEnUqtdgY1bATGz3mw33nH9Y+dmA+w5DHvUmBgkr5rM/KCHpCsiFNRUfokW/szpPPgMK2hm4A==",
+      "version": "1.10.0",
+      "resolved": "https://registry.npmjs.org/aws4/-/aws4-1.10.0.tgz",
+      "integrity": "sha512-3YDiu347mtVtjpyV3u5kVqQLP242c06zwDOgpeRnybmXlYYsLbtTrUBUm8i8srONt+FWobl5aibnU1030PeeuA==",
       "dev": true
     },
     "axobject-query": {
@@ -1008,101 +2742,165 @@
       }
     },
     "babel-jest": {
-      "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.9.0",
-        "@jest/types": "^24.9.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": {
-      "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==",
-      "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"
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-26.2.2.tgz",
+      "integrity": "sha512-JmLuePHgA+DSOdOL8lPxCgD2LhPPm+rdw1vnxR73PpIrnmKCS2/aBhtkAcxQWuUcW2hBrH8MJ3LKXE7aWpNZyA==",
+      "dev": true,
+      "requires": {
+        "@jest/transform": "^26.2.2",
+        "@jest/types": "^26.2.0",
+        "@types/babel__core": "^7.1.7",
+        "babel-plugin-istanbul": "^6.0.0",
+        "babel-preset-jest": "^26.2.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.4",
+        "slash": "^3.0.0"
       },
       "dependencies": {
-        "find-up": {
-          "version": "3.0.0",
-          "resolved": "https://registry.npmjs.org/find-up/-/find-up-3.0.0.tgz",
-          "integrity": "sha512-1yD6RmLI1XBfxugvORwlck6f75tYL+iR0jqwsOrOxMZyGYqUuDhJ0l4AXdO1iX/FTs9cBAMEk1gWSEx1kSbylg==",
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
           "dev": true,
           "requires": {
-            "locate-path": "^3.0.0"
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.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==",
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
           "dev": true,
           "requires": {
-            "p-locate": "^3.0.0",
-            "path-exists": "^3.0.0"
+            "@types/yargs-parser": "*"
           }
         },
-        "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==",
+        "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": {
-            "p-try": "^2.0.0"
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
           }
         },
-        "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==",
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
           "dev": true,
           "requires": {
-            "p-limit": "^2.0.0"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.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==",
+        "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
           "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
+        },
+        "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"
+          }
         }
       }
     },
+    "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"
+      }
+    },
     "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": "26.2.0",
+      "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-26.2.0.tgz",
+      "integrity": "sha512-B/hVMRv8Nh1sQ1a3EY8I0n4Y1Wty3NrR5ebOyVT302op+DOAau+xNEImGMsUWOC3++ZlMooCytKz+NgN8aKGbA==",
       "dev": true,
       "requires": {
+        "@babel/template": "^7.3.3",
+        "@babel/types": "^7.3.3",
+        "@types/babel__core": "^7.0.0",
         "@types/babel__traverse": "^7.0.6"
       }
     },
+    "babel-preset-current-node-syntax": {
+      "version": "0.1.3",
+      "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-0.1.3.tgz",
+      "integrity": "sha512-uyexu1sVwcdFnyq9o8UQYsXwXflIh8LvrF5+cKrYam93ned1CStffB3+BEcsxGSgagoA3GEyjDqO4a/58hyPYQ==",
+      "dev": true,
+      "requires": {
+        "@babel/plugin-syntax-async-generators": "^7.8.4",
+        "@babel/plugin-syntax-bigint": "^7.8.3",
+        "@babel/plugin-syntax-class-properties": "^7.8.3",
+        "@babel/plugin-syntax-import-meta": "^7.8.3",
+        "@babel/plugin-syntax-json-strings": "^7.8.3",
+        "@babel/plugin-syntax-logical-assignment-operators": "^7.8.3",
+        "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3",
+        "@babel/plugin-syntax-numeric-separator": "^7.8.3",
+        "@babel/plugin-syntax-object-rest-spread": "^7.8.3",
+        "@babel/plugin-syntax-optional-catch-binding": "^7.8.3",
+        "@babel/plugin-syntax-optional-chaining": "^7.8.3"
+      }
+    },
     "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": "26.2.0",
+      "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-26.2.0.tgz",
+      "integrity": "sha512-R1k8kdP3R9phYQugXeNnK/nvCGlBzG4m3EoIIukC80GXb6wCv2XiwPhK6K9MAkQcMszWBYvl2Wm+yigyXFQqXg==",
       "dev": true,
       "requires": {
-        "@babel/plugin-syntax-object-rest-spread": "^7.0.0",
-        "babel-plugin-jest-hoist": "^24.9.0"
+        "babel-plugin-jest-hoist": "^26.2.0",
+        "babel-preset-current-node-syntax": "^0.1.2"
       }
     },
+    "backo2": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/backo2/-/backo2-1.0.2.tgz",
+      "integrity": "sha1-MasayLEpNjRj41s+u2n038+6eUc=",
+      "dev": true
+    },
     "balanced-match": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.0.tgz",
-      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c=",
-      "dev": true
+      "integrity": "sha1-ibTRmasr7kneFk6gK4nORi1xt2c="
     },
     "base": {
       "version": "0.11.2",
@@ -1177,64 +2975,26 @@
       "version": "1.1.11",
       "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz",
       "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==",
-      "dev": true,
       "requires": {
         "balanced-match": "^1.0.0",
         "concat-map": "0.0.1"
       }
     },
     "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"
-          }
-        }
+        "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": {
-      "version": "1.11.3",
-      "resolved": "https://registry.npmjs.org/browser-resolve/-/browser-resolve-1.11.3.tgz",
-      "integrity": "sha512-exDi1BYWB/6raKHmDTCicQfTkqwN5fioMFV4j8BsfMU4R2DK/QfZfK7kOVkmWCNANf0snkBzqGqAJBao9gZMdQ==",
-      "dev": true,
-      "requires": {
-        "resolve": "1.1.7"
-      },
-      "dependencies": {
-        "resolve": {
-          "version": "1.1.7",
-          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.1.7.tgz",
-          "integrity": "sha1-IDEU2CrSxe2ejgQRs5ModeiJ6Xs=",
-          "dev": true
-        }
-      }
-    },
     "bs-logger": {
       "version": "0.2.6",
       "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz",
@@ -1287,6 +3047,16 @@
       "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==",
       "dev": true
     },
+    "camel-case": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/camel-case/-/camel-case-4.1.1.tgz",
+      "integrity": "sha512-7fa2WcG4fYFkclIvEmxBbTvmibwF2/agfEBc6q3lOpVu0A13ltLsA+Hr/8Hp6kp5f+G7hKi6t8lys6XxP+1K6Q==",
+      "dev": true,
+      "requires": {
+        "pascal-case": "^3.1.1",
+        "tslib": "^1.10.0"
+      }
+    },
     "camelcase": {
       "version": "5.3.1",
       "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz",
@@ -1319,6 +3089,12 @@
         "supports-color": "^5.3.0"
       }
     },
+    "char-regex": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz",
+      "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==",
+      "dev": true
+    },
     "chardet": {
       "version": "0.7.0",
       "resolved": "https://registry.npmjs.org/chardet/-/chardet-0.7.0.tgz",
@@ -1354,6 +3130,12 @@
         }
       }
     },
+    "clean-stack": {
+      "version": "2.2.0",
+      "resolved": "https://registry.npmjs.org/clean-stack/-/clean-stack-2.2.0.tgz",
+      "integrity": "sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==",
+      "dev": true
+    },
     "cli-cursor": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/cli-cursor/-/cli-cursor-2.1.0.tgz",
@@ -1370,34 +3152,52 @@
       "dev": true
     },
     "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"
       },
       "dependencies": {
+        "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
+        },
+        "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
+        },
+        "is-fullwidth-code-point": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz",
+          "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==",
+          "dev": true
+        },
         "string-width": {
-          "version": "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"
           }
         }
       }
@@ -1408,6 +3208,12 @@
       "integrity": "sha1-bqa989hTrlTMuOR7+gvz+QMfsYQ=",
       "dev": true
     },
+    "collect-v8-coverage": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.1.tgz",
+      "integrity": "sha512-iBPtljfCNcTKNAto0KEtDfZ3qzjJvqE3aTGZsbhjSBlorqpXJlaWWtPO35D+ZImoC3KWejX64o+yPGxhWSTzfg==",
+      "dev": true
+    },
     "collection-visit": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/collection-visit/-/collection-visit-1.0.0.tgz",
@@ -1457,8 +3263,7 @@
     "concat-map": {
       "version": "0.0.1",
       "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz",
-      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=",
-      "dev": true
+      "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s="
     },
     "contains-path": {
       "version": "0.1.0",
@@ -1493,14 +3298,46 @@
       "integrity": "sha1-tf1UIgqivFq1eqtxQMlAdUUDwac=",
       "dev": true
     },
+    "cosmiconfig": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-6.0.0.tgz",
+      "integrity": "sha512-xb3ZL6+L8b9JLLCx3ZdoZy4+2ECphCMo2PwqgP1tlfVq6M6YReyzBJtvWWtbDSpNr9hn96pkCiZqUcFEc+54Qg==",
+      "dev": true,
+      "requires": {
+        "@types/parse-json": "^4.0.0",
+        "import-fresh": "^3.1.0",
+        "parse-json": "^5.0.0",
+        "path-type": "^4.0.0",
+        "yaml": "^1.7.2"
+      },
+      "dependencies": {
+        "parse-json": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz",
+          "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.0.0",
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1",
+            "lines-and-columns": "^1.1.6"
+          }
+        },
+        "path-type": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+          "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+          "dev": true
+        }
+      }
+    },
     "cross-fetch": {
-      "version": "2.2.2",
-      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-2.2.2.tgz",
-      "integrity": "sha1-pH/09/xxLauo9qaVoRyUhEDUVyM=",
+      "version": "3.0.5",
+      "resolved": "https://registry.npmjs.org/cross-fetch/-/cross-fetch-3.0.5.tgz",
+      "integrity": "sha512-FFLcLtraisj5eteosnX1gf01qYDCOc4fDy0+euOt8Kn9YBY2NtXL/pCoYPavw24NIQkQqm5ZOLsGD5Zzj0gyew==",
       "dev": true,
       "requires": {
-        "node-fetch": "2.1.2",
-        "whatwg-fetch": "2.0.4"
+        "node-fetch": "2.6.0"
       }
     },
     "cross-spawn": {
@@ -1523,18 +3360,36 @@
       }
     },
     "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.3.0",
+      "resolved": "https://registry.npmjs.org/cssstyle/-/cssstyle-2.3.0.tgz",
+      "integrity": "sha512-AZL67abkUzIuvcHqk7c09cezpGNcxUxU4Ioi/05xHk4DQeTkWmGYftIE6ctU6AEt+Gn4n1lDStOtj7FKycP71A==",
+      "dev": true,
+      "requires": {
+        "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
+        }
+      }
+    },
+    "d": {
+      "version": "1.0.1",
+      "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz",
+      "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==",
       "dev": true,
       "requires": {
-        "cssom": "0.3.x"
+        "es5-ext": "^0.10.50",
+        "type": "^1.0.1"
       }
     },
     "damerau-levenshtein": {
@@ -1553,27 +3408,14 @@
       }
     },
     "data-urls": {
-      "version": "1.1.0",
-      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-1.1.0.tgz",
-      "integrity": "sha512-YTWYI9se1P55u58gL5GkQHW4P6VJBJ5iBT+B5a7i2Tjadhv52paJG0qHX4A0OR6/t52odI64KP2YvFpkDOi3eQ==",
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/data-urls/-/data-urls-2.0.0.tgz",
+      "integrity": "sha512-X5eWTSXO/BJmpdIKCRuKUgSCgAN0OwliVK3yPKbwIWU1Tdw5BRajxlzMidvh+gwko9AfQ9zIj52pzF91Q3YAvQ==",
       "dev": true,
       "requires": {
-        "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"
-          }
-        }
+        "abab": "^2.0.3",
+        "whatwg-mimetype": "^2.3.0",
+        "whatwg-url": "^8.0.0"
       }
     },
     "debug": {
@@ -1591,18 +3433,36 @@
       "integrity": "sha1-9lNNFRSCabIDUue+4m9QH5oZEpA=",
       "dev": true
     },
+    "decimal.js": {
+      "version": "10.2.0",
+      "resolved": "https://registry.npmjs.org/decimal.js/-/decimal.js-10.2.0.tgz",
+      "integrity": "sha512-vDPw+rDgn3bZe1+F/pyEwb1oMG2XTlRVgAa6B4KccTEpYgF8w6eQllVbQcfIJnZyvzFtFpxnpGtx8dd7DJp/Rw==",
+      "dev": true
+    },
     "decode-uri-component": {
       "version": "0.2.0",
       "resolved": "https://registry.npmjs.org/decode-uri-component/-/decode-uri-component-0.2.0.tgz",
       "integrity": "sha1-6zkTMzRYd1y4TNGh+uBiEGu4dUU=",
       "dev": true
     },
+    "dedent": {
+      "version": "0.7.0",
+      "resolved": "https://registry.npmjs.org/dedent/-/dedent-0.7.0.tgz",
+      "integrity": "sha1-JJXduvbrh0q7Dhvp3yLS5aVEMmw=",
+      "dev": true
+    },
     "deep-is": {
       "version": "0.1.3",
       "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz",
       "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=",
       "dev": true
     },
+    "deepmerge": {
+      "version": "4.2.2",
+      "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.2.2.tgz",
+      "integrity": "sha512-FJ3UgI4gIl+PHZm53knsuSFpE+nESMr7M4v9QcgB7S63Kj/6WqMiFQJpBBYz1Pt+66bZpP3Q7Lye0Oo9MPKEdg==",
+      "dev": true
+    },
     "define-properties": {
       "version": "1.1.3",
       "resolved": "https://registry.npmjs.org/define-properties/-/define-properties-1.1.3.tgz",
@@ -1665,9 +3525,9 @@
       "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": {
@@ -1676,6 +3536,23 @@
       "integrity": "sha512-Dj6Wk3tWyTE+Fo1rW8v0Xhwk80um6yFYKbuAxc9c3EZxIHFDYwbi34Uk42u1CdnIiVorvt4RmlSDjIPyzGC2ew==",
       "dev": true
     },
+    "dir-glob": {
+      "version": "3.0.1",
+      "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz",
+      "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==",
+      "dev": true,
+      "requires": {
+        "path-type": "^4.0.0"
+      },
+      "dependencies": {
+        "path-type": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz",
+          "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==",
+          "dev": true
+        }
+      }
+    },
     "doctrine": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz",
@@ -1686,12 +3563,20 @@
       }
     },
     "domexception": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/domexception/-/domexception-1.0.1.tgz",
-      "integrity": "sha512-raigMkn7CJNNo6Ihro1fzG7wr3fHuYVytzquZKX5n0yizGsTcYgzdIUwj1X9pK0VvjeihV+XiclP+DjwbsSKug==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/domexception/-/domexception-2.0.1.tgz",
+      "integrity": "sha512-yxJ2mFy/sibVQlu5qHjOkf9J3K6zgmCxgJ94u2EdvDOV09H+32LtRswEcUsmUWN72pVLOEnTSRaIVVzVQgS0dg==",
       "dev": true,
       "requires": {
-        "webidl-conversions": "^4.0.2"
+        "webidl-conversions": "^5.0.0"
+      },
+      "dependencies": {
+        "webidl-conversions": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-5.0.0.tgz",
+          "integrity": "sha512-VlZwKPCkYKxQgeSbH5EyngOmRp7Ww7I9rQLERETtf5ofd9pGeswWiOtogpEO850jziPRarreGxn5QIiTqpb2wA==",
+          "dev": true
+        }
       }
     },
     "ecc-jsbn": {
@@ -1704,6 +3589,12 @@
         "safer-buffer": "^2.1.0"
       }
     },
+    "emittery": {
+      "version": "0.7.1",
+      "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.7.1.tgz",
+      "integrity": "sha512-d34LN4L6h18Bzz9xpoku2nPwKxCPlPMr3EEKTkoEBi+1/+b0lcRkRJ1UVyyZaKNeqGR3swcGl6s390DNO4YVgQ==",
+      "dev": true
+    },
     "emoji-regex": {
       "version": "7.0.3",
       "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-7.0.3.tgz",
@@ -1756,6 +3647,38 @@
         "is-symbol": "^1.0.2"
       }
     },
+    "es5-ext": {
+      "version": "0.10.53",
+      "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.53.tgz",
+      "integrity": "sha512-Xs2Stw6NiNHWypzRTY1MtaG/uJlwCk8kH81920ma8mvN8Xq1gsfhZvpkImLQArw8AHnv8MT2I45J3c0R8slE+Q==",
+      "dev": true,
+      "requires": {
+        "es6-iterator": "~2.0.3",
+        "es6-symbol": "~3.1.3",
+        "next-tick": "~1.0.0"
+      }
+    },
+    "es6-iterator": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz",
+      "integrity": "sha1-p96IkUGgWpSwhUQDstCg+/qY87c=",
+      "dev": true,
+      "requires": {
+        "d": "1",
+        "es5-ext": "^0.10.35",
+        "es6-symbol": "^3.1.1"
+      }
+    },
+    "es6-symbol": {
+      "version": "3.1.3",
+      "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz",
+      "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==",
+      "dev": true,
+      "requires": {
+        "d": "^1.0.1",
+        "ext": "^1.1.2"
+      }
+    },
     "escape-string-regexp": {
       "version": "1.0.5",
       "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz",
@@ -1763,24 +3686,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.3",
+      "resolved": "https://registry.npmjs.org/escodegen/-/escodegen-1.14.3.tgz",
+      "integrity": "sha512-qFcX0XJkdg+PB3xjZZG/wKSuT1PnQWx57+TVSjIMmILd2yC/6ByYElPwJnslDsuWuSAp4AwJGumarAAmJch5Kw==",
       "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",
@@ -2026,13 +3943,32 @@
       }
     },
     "eslint-plugin-graphql": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-3.1.0.tgz",
-      "integrity": "sha512-87HGS00aeBqGFiQZQGzSPzk1D59w+124F8CRIDATh3LJqce5RCTuUI4tcIqPeyY95YPBCIKwISksWUuA0nrgNw==",
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-graphql/-/eslint-plugin-graphql-4.0.0.tgz",
+      "integrity": "sha512-d5tQm24YkVvCEk29ZR5ScsgXqAGCjKlMS8lx3mS7FS/EKsWbkvXQImpvic03EpMIvNTBW5e+2xnHzXB/VHNZJw==",
       "dev": true,
       "requires": {
-        "graphql-config": "^2.0.1",
-        "lodash": "^4.11.1"
+        "@babel/runtime": "^7.10.0",
+        "graphql-config": "^3.0.2",
+        "lodash.flatten": "^4.4.0",
+        "lodash.without": "^4.4.0"
+      },
+      "dependencies": {
+        "@babel/runtime": {
+          "version": "7.11.2",
+          "resolved": "https://registry.npmjs.org/@babel/runtime/-/runtime-7.11.2.tgz",
+          "integrity": "sha512-TeWkU52so0mPtDcaCTxNBI/IHiz0pZgr8VEFqXFtZWpYD08ZB6FaSwVAS8MKRQAP3bYKiVjwysOJgMFY28o6Tw==",
+          "dev": true,
+          "requires": {
+            "regenerator-runtime": "^0.13.4"
+          }
+        },
+        "regenerator-runtime": {
+          "version": "0.13.7",
+          "resolved": "https://registry.npmjs.org/regenerator-runtime/-/regenerator-runtime-0.13.7.tgz",
+          "integrity": "sha512-a54FxoJDIr27pgf7IgeQGxmqUNYrcV338lf/6gH456HZ/PhX+5BcwHXG9ajESmwe6WRO0tAzRUrRmNONWgkrew==",
+          "dev": true
+        }
       }
     },
     "eslint-plugin-import": {
@@ -2097,1075 +4033,710 @@
       "integrity": "sha512-OaqnSS7uBgcGiqXUiEnjoqxPNKvR4JWG5mSRkzVoR6+vDwlqqp11beeql1hYs0HTbdhiwrxWLxbX0Vx7roG3Ew==",
       "dev": true,
       "requires": {
-        "@typescript-eslint/experimental-utils": "^1.13.0"
-      },
-      "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/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-scope": {
-          "version": "4.0.3",
-          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
-          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
-          "dev": true,
-          "requires": {
-            "esrecurse": "^4.1.0",
-            "estraverse": "^4.1.1"
-          }
-        },
-        "semver": {
-          "version": "5.5.0",
-          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
-          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
-          "dev": true
-        }
-      }
-    },
-    "eslint-plugin-jsx-a11y": {
-      "version": "6.2.3",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
-      "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
-      "dev": true,
-      "requires": {
-        "@babel/runtime": "^7.4.5",
-        "aria-query": "^3.0.0",
-        "array-includes": "^3.0.3",
-        "ast-types-flow": "^0.0.7",
-        "axobject-query": "^2.0.2",
-        "damerau-levenshtein": "^1.0.4",
-        "emoji-regex": "^7.0.2",
-        "has": "^1.0.3",
-        "jsx-ast-utils": "^2.2.1"
-      }
-    },
-    "eslint-plugin-prettier": {
-      "version": "3.1.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz",
-      "integrity": "sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA==",
-      "dev": true,
-      "requires": {
-        "prettier-linter-helpers": "^1.0.0"
-      }
-    },
-    "eslint-plugin-react": {
-      "version": "7.17.0",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.17.0.tgz",
-      "integrity": "sha512-ODB7yg6lxhBVMeiH1c7E95FLD4E/TwmFjltiU+ethv7KPdCwgiFuOZg9zNRHyufStTDLl/dEFqI2Q1VPmCd78A==",
-      "dev": true,
-      "requires": {
-        "array-includes": "^3.0.3",
-        "doctrine": "^2.1.0",
-        "eslint-plugin-eslint-plugin": "^2.1.0",
-        "has": "^1.0.3",
-        "jsx-ast-utils": "^2.2.3",
-        "object.entries": "^1.1.0",
-        "object.fromentries": "^2.0.1",
-        "object.values": "^1.1.0",
-        "prop-types": "^15.7.2",
-        "resolve": "^1.13.1"
-      },
-      "dependencies": {
-        "doctrine": {
-          "version": "2.1.0",
-          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
-          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
-          "dev": true,
-          "requires": {
-            "esutils": "^2.0.2"
-          }
-        }
-      }
-    },
-    "eslint-plugin-relay": {
-      "version": "1.4.1",
-      "resolved": "https://registry.npmjs.org/eslint-plugin-relay/-/eslint-plugin-relay-1.4.1.tgz",
-      "integrity": "sha512-yb+p+4AxZTi2gXN7cZRfXMBFlRa5j6TtiVeq3yHXyy+tlgYNpxi/dDrP1+tcUTNP9vdaJovnfGZ5jp6kMiH9eg==",
-      "dev": true,
-      "requires": {
-        "graphql": "^14.0.0"
-      }
-    },
-    "eslint-rule-documentation": {
-      "version": "1.0.23",
-      "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz",
-      "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==",
-      "dev": true
-    },
-    "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": "1.4.3",
-      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
-      "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
-      "dev": true,
-      "requires": {
-        "eslint-visitor-keys": "^1.1.0"
-      }
-    },
-    "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
-    },
-    "espree": {
-      "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
-      "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
-      "dev": true,
-      "requires": {
-        "acorn": "^6.0.7",
-        "acorn-jsx": "^5.0.0",
-        "eslint-visitor-keys": "^1.0.0"
-      }
-    },
-    "esprima": {
-      "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
-    },
-    "esquery": {
-      "version": "1.0.1",
-      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.0.1.tgz",
-      "integrity": "sha512-SmiyZ5zIWH9VM+SRUReLS5Q8a7GxtRdxEBVZpm98rJM7Sb+A9DVCndXfkeFUd3byderg+EbDkfnevfCwynWaNA==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.0.0"
-      }
-    },
-    "esrecurse": {
-      "version": "4.2.1",
-      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
-      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
-      "dev": true,
-      "requires": {
-        "estraverse": "^4.1.0"
-      }
-    },
-    "estraverse": {
-      "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.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.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": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
-      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
-      "requires": {
-        "cross-spawn": "^6.0.0",
-        "get-stream": "^4.0.0",
-        "is-stream": "^1.1.0",
-        "npm-run-path": "^2.0.0",
-        "p-finally": "^1.0.0",
-        "signal-exit": "^3.0.0",
-        "strip-eof": "^1.0.0"
-      }
-    },
-    "exit": {
-      "version": "0.1.2",
-      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
-      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
-      "dev": true
-    },
-    "expand-brackets": {
-      "version": "2.1.4",
-      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
-      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
-      "dev": true,
-      "requires": {
-        "debug": "^2.3.3",
-        "define-property": "^0.2.5",
-        "extend-shallow": "^2.0.1",
-        "posix-character-classes": "^0.1.0",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
+        "@typescript-eslint/experimental-utils": "^1.13.0"
       },
       "dependencies": {
-        "debug": {
-          "version": "2.6.9",
-          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
-          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
+        "@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": {
-            "ms": "2.0.0"
+            "@types/json-schema": "^7.0.3",
+            "@typescript-eslint/typescript-estree": "1.13.0",
+            "eslint-scope": "^4.0.0"
           }
         },
-        "define-property": {
-          "version": "0.2.5",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
-          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
+        "@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": {
-            "is-descriptor": "^0.1.0"
+            "lodash.unescape": "4.0.1",
+            "semver": "5.5.0"
           }
         },
-        "extend-shallow": {
-          "version": "2.0.1",
-          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
-          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
+        "eslint-scope": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-4.0.3.tgz",
+          "integrity": "sha512-p7VutNr1O/QrxysMo3E45FjYDTeXBy0iTltPFNSqKAIfjDSXC+4dj+qfyuD8bfAXrW/y6lW3O76VaYNPKfpKrg==",
           "dev": true,
           "requires": {
-            "is-extendable": "^0.1.0"
+            "esrecurse": "^4.1.0",
+            "estraverse": "^4.1.1"
           }
         },
-        "ms": {
-          "version": "2.0.0",
-          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
-          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+        "semver": {
+          "version": "5.5.0",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-5.5.0.tgz",
+          "integrity": "sha512-4SJ3dm0WAwWy/NVeioZh5AntkdJoWKxHxcmyP622fOkgHa4z3R0TdBJICINyaSDE6uNwVc8gZr+ZinwZAH4xIA==",
           "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"
-      }
-    },
-    "extend": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
-      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
-      "dev": true
-    },
-    "extend-shallow": {
-      "version": "3.0.2",
-      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
-      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+    "eslint-plugin-jsx-a11y": {
+      "version": "6.2.3",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-jsx-a11y/-/eslint-plugin-jsx-a11y-6.2.3.tgz",
+      "integrity": "sha512-CawzfGt9w83tyuVekn0GDPU9ytYtxyxyFZ3aSWROmnRRFQFT2BiPJd7jvRdzNDi6oLWaS2asMeYSNMjWTV4eNg==",
       "dev": true,
       "requires": {
-        "assign-symbols": "^1.0.0",
-        "is-extendable": "^1.0.1"
-      },
-      "dependencies": {
-        "is-extendable": {
-          "version": "1.0.1",
-          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
-          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
-          "dev": true,
-          "requires": {
-            "is-plain-object": "^2.0.4"
-          }
-        }
+        "@babel/runtime": "^7.4.5",
+        "aria-query": "^3.0.0",
+        "array-includes": "^3.0.3",
+        "ast-types-flow": "^0.0.7",
+        "axobject-query": "^2.0.2",
+        "damerau-levenshtein": "^1.0.4",
+        "emoji-regex": "^7.0.2",
+        "has": "^1.0.3",
+        "jsx-ast-utils": "^2.2.1"
       }
     },
-    "external-editor": {
-      "version": "3.1.0",
-      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
-      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+    "eslint-plugin-prettier": {
+      "version": "3.1.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-prettier/-/eslint-plugin-prettier-3.1.1.tgz",
+      "integrity": "sha512-A+TZuHZ0KU0cnn56/9mfR7/KjUJ9QNVXUhwvRFSR7PGPe0zQR6PTkmyqg1AtUUEOzTqeRsUwyKFh0oVZKVCrtA==",
       "dev": true,
       "requires": {
-        "chardet": "^0.7.0",
-        "iconv-lite": "^0.4.24",
-        "tmp": "^0.0.33"
+        "prettier-linter-helpers": "^1.0.0"
       }
     },
-    "extglob": {
-      "version": "2.0.4",
-      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
-      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+    "eslint-plugin-react": {
+      "version": "7.17.0",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-react/-/eslint-plugin-react-7.17.0.tgz",
+      "integrity": "sha512-ODB7yg6lxhBVMeiH1c7E95FLD4E/TwmFjltiU+ethv7KPdCwgiFuOZg9zNRHyufStTDLl/dEFqI2Q1VPmCd78A==",
       "dev": true,
       "requires": {
-        "array-unique": "^0.3.2",
-        "define-property": "^1.0.0",
-        "expand-brackets": "^2.1.4",
-        "extend-shallow": "^2.0.1",
-        "fragment-cache": "^0.2.1",
-        "regex-not": "^1.0.0",
-        "snapdragon": "^0.8.1",
-        "to-regex": "^3.0.1"
+        "array-includes": "^3.0.3",
+        "doctrine": "^2.1.0",
+        "eslint-plugin-eslint-plugin": "^2.1.0",
+        "has": "^1.0.3",
+        "jsx-ast-utils": "^2.2.3",
+        "object.entries": "^1.1.0",
+        "object.fromentries": "^2.0.1",
+        "object.values": "^1.1.0",
+        "prop-types": "^15.7.2",
+        "resolve": "^1.13.1"
       },
       "dependencies": {
-        "define-property": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
-          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
-          "dev": true,
-          "requires": {
-            "is-descriptor": "^1.0.0"
-          }
-        },
-        "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-accessor-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
-          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-data-descriptor": {
-          "version": "1.0.0",
-          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
-          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
-          "dev": true,
-          "requires": {
-            "kind-of": "^6.0.0"
-          }
-        },
-        "is-descriptor": {
-          "version": "1.0.2",
-          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
-          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
+        "doctrine": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-2.1.0.tgz",
+          "integrity": "sha512-35mSku4ZXK0vfCuHEDAwt55dg2jNajHZ1odvF+8SSr82EsZY4QmXfuWso8oEd8zRhVObSN18aM0CjSdoBX7zIw==",
           "dev": true,
           "requires": {
-            "is-accessor-descriptor": "^1.0.0",
-            "is-data-descriptor": "^1.0.0",
-            "kind-of": "^6.0.2"
+            "esutils": "^2.0.2"
           }
         }
       }
     },
-    "extsprintf": {
-      "version": "1.3.0",
-      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
-      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
-      "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=",
-      "dev": true
-    },
-    "fast-diff": {
-      "version": "1.2.0",
-      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
-      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
-      "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=",
-      "dev": true
+    "eslint-plugin-relay": {
+      "version": "1.4.1",
+      "resolved": "https://registry.npmjs.org/eslint-plugin-relay/-/eslint-plugin-relay-1.4.1.tgz",
+      "integrity": "sha512-yb+p+4AxZTi2gXN7cZRfXMBFlRa5j6TtiVeq3yHXyy+tlgYNpxi/dDrP1+tcUTNP9vdaJovnfGZ5jp6kMiH9eg==",
+      "dev": true,
+      "requires": {
+        "graphql": "^14.0.0"
+      }
     },
-    "fast-levenshtein": {
-      "version": "2.0.6",
-      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
-      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+    "eslint-rule-documentation": {
+      "version": "1.0.23",
+      "resolved": "https://registry.npmjs.org/eslint-rule-documentation/-/eslint-rule-documentation-1.0.23.tgz",
+      "integrity": "sha512-pWReu3fkohwyvztx/oQWWgld2iad25TfUdi6wvhhaDPIQjHU/pyvlKgXFw1kX31SQK2Nq9MH+vRDWB0ZLy8fYw==",
       "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=",
+    "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": {
-        "bser": "^2.0.0"
+        "esrecurse": "^4.1.0",
+        "estraverse": "^4.1.1"
       }
     },
-    "figures": {
-      "version": "2.0.0",
-      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
-      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+    "eslint-utils": {
+      "version": "1.4.3",
+      "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-1.4.3.tgz",
+      "integrity": "sha512-fbBN5W2xdY45KulGXmLHZ3c3FHfVYmKg0IrAKGOkT/464PQsx2UeIzfz1RmEci+KLm1bBaAzZAh8+/E+XAeZ8Q==",
       "dev": true,
       "requires": {
-        "escape-string-regexp": "^1.0.5"
+        "eslint-visitor-keys": "^1.1.0"
       }
     },
-    "file-entry-cache": {
+    "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
+    },
+    "espree": {
       "version": "5.0.1",
-      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
-      "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+      "resolved": "https://registry.npmjs.org/espree/-/espree-5.0.1.tgz",
+      "integrity": "sha512-qWAZcWh4XE/RwzLJejfcofscgMc9CamR6Tn1+XRXNzrvUSSbiAjGOI/fggztjIi7y9VLPqnICMIPiGyr8JaZ0A==",
       "dev": true,
       "requires": {
-        "flat-cache": "^2.0.1"
+        "acorn": "^6.0.7",
+        "acorn-jsx": "^5.0.0",
+        "eslint-visitor-keys": "^1.0.0"
       }
     },
-    "fill-range": {
-      "version": "4.0.0",
-      "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-4.0.0.tgz",
-      "integrity": "sha1-1USBHUKPmOsGpj3EAtJAPDKMOPc=",
+    "esprima": {
+      "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
+    },
+    "esquery": {
+      "version": "1.3.1",
+      "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.3.1.tgz",
+      "integrity": "sha512-olpvt9QG0vniUBZspVRN6lwB7hOZoTRtT+jzR+tS4ffYx2mzbw+z0XCOk44aaLYKApNX5nMm+E+P6o25ip/DHQ==",
       "dev": true,
       "requires": {
-        "extend-shallow": "^2.0.1",
-        "is-number": "^3.0.0",
-        "repeat-string": "^1.6.1",
-        "to-regex-range": "^2.1.0"
+        "estraverse": "^5.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"
-          }
+        "estraverse": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz",
+          "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==",
+          "dev": true
         }
       }
     },
-    "find-up": {
-      "version": "2.1.0",
-      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
-      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
-      "dev": true,
-      "requires": {
-        "locate-path": "^2.0.0"
-      }
-    },
-    "flat-cache": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
-      "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+    "esrecurse": {
+      "version": "4.2.1",
+      "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.2.1.tgz",
+      "integrity": "sha512-64RBB++fIOAXPw3P9cy89qfMlvZEXZkqqJkjqqXIvzP5ezRZjW+lPWjw35UX/3EhUPFYbg5ER4JYgDw4007/DQ==",
       "dev": true,
       "requires": {
-        "flatted": "^2.0.0",
-        "rimraf": "2.6.3",
-        "write": "1.0.3"
+        "estraverse": "^4.1.0"
       }
     },
-    "flatted": {
-      "version": "2.0.1",
-      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.1.tgz",
-      "integrity": "sha512-a1hQMktqW9Nmqr5aktAux3JMNqaucxGcjtjWnZLHX7yyPCmlSV3M54nGYbqT8K+0GhF3NBgmJCc3ma+WOgX8Jg==",
+    "estraverse": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz",
+      "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
       "dev": true
     },
-    "for-in": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
-      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+    "esutils": {
+      "version": "2.0.3",
+      "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz",
+      "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==",
       "dev": true
     },
-    "forever-agent": {
-      "version": "0.6.1",
-      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
-      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+    "eventemitter3": {
+      "version": "3.1.2",
+      "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-3.1.2.tgz",
+      "integrity": "sha512-tvtQIeLVHjDkJYnzf2dgVMxfuSGJeM/7UCG17TT4EumTfNtF+0nebF/4zWOIkCreAbtNqhGEboB6BWrwqNaw4Q==",
       "dev": true
     },
-    "form-data": {
-      "version": "2.3.3",
-      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
-      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
-      "dev": true,
-      "requires": {
-        "asynckit": "^0.4.0",
-        "combined-stream": "^1.0.6",
-        "mime-types": "^2.1.12"
-      }
+    "exec-sh": {
+      "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
     },
-    "fragment-cache": {
-      "version": "0.2.1",
-      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
-      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
-      "dev": true,
+    "execa": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-1.0.0.tgz",
+      "integrity": "sha512-adbxcyWV46qiHyvSp50TKt05tB4tK3HcmF7/nxfAdhnox83seTDbwnaqKO4sXRy7roHAIFqJP/Rw/AuEbX61LA==",
       "requires": {
-        "map-cache": "^0.2.2"
+        "cross-spawn": "^6.0.0",
+        "get-stream": "^4.0.0",
+        "is-stream": "^1.1.0",
+        "npm-run-path": "^2.0.0",
+        "p-finally": "^1.0.0",
+        "signal-exit": "^3.0.0",
+        "strip-eof": "^1.0.0"
       }
     },
-    "fs.realpath": {
-      "version": "1.0.0",
-      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
-      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=",
+    "exit": {
+      "version": "0.1.2",
+      "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz",
+      "integrity": "sha1-BjJjj42HfMghB9MKD/8aF8uhzQw=",
       "dev": true
     },
-    "fsevents": {
-      "version": "1.2.9",
-      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-1.2.9.tgz",
-      "integrity": "sha512-oeyj2H3EjjonWcFjD5NvZNE9Rqe4UW+nQBU2HNeKw0koVLEFIhtyETyAakeAM3de7Z/SW5kcA+fZUait9EApnw==",
+    "expand-brackets": {
+      "version": "2.1.4",
+      "resolved": "https://registry.npmjs.org/expand-brackets/-/expand-brackets-2.1.4.tgz",
+      "integrity": "sha1-t3c14xXOMPa27/D4OwQVGiJEliI=",
       "dev": true,
-      "optional": true,
       "requires": {
-        "nan": "^2.12.1",
-        "node-pre-gyp": "^0.12.0"
+        "debug": "^2.3.3",
+        "define-property": "^0.2.5",
+        "extend-shallow": "^2.0.1",
+        "posix-character-classes": "^0.1.0",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
       },
       "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,
+          "version": "2.6.9",
+          "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz",
+          "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==",
           "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"
+            "ms": "2.0.0"
           }
         },
-        "glob": {
-          "version": "7.1.3",
-          "bundled": true,
+        "define-property": {
+          "version": "0.2.5",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-0.2.5.tgz",
+          "integrity": "sha1-w1se+RjsPJkPmlvFe+BKrOxcgRY=",
           "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"
+            "is-descriptor": "^0.1.0"
           }
         },
-        "has-unicode": {
+        "extend-shallow": {
           "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,
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
-          "optional": true,
           "requires": {
-            "minimist": "0.0.8"
+            "is-extendable": "^0.1.0"
           }
         },
         "ms": {
-          "version": "2.1.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "needle": {
-          "version": "2.3.0",
-          "bundled": true,
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz",
+          "integrity": "sha1-VgiurfwAvmwpAd9fmGF4jeDVl8g=",
+          "dev": true
+        }
+      }
+    },
+    "expect": {
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/expect/-/expect-26.2.0.tgz",
+      "integrity": "sha512-8AMBQ9UVcoUXt0B7v+5/U5H6yiUR87L6eKCfjE3spx7Ya5lF+ebUo37MCFBML2OiLfkX1sxmQOZhIDonyVTkcw==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^26.2.0",
+        "ansi-styles": "^4.0.0",
+        "jest-get-type": "^26.0.0",
+        "jest-matcher-utils": "^26.2.0",
+        "jest-message-util": "^26.2.0",
+        "jest-regex-util": "^26.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "debug": "^4.1.0",
-            "iconv-lite": "^0.4.4",
-            "sax": "^1.2.4"
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
           }
         },
-        "node-pre-gyp": {
-          "version": "0.12.0",
-          "bundled": true,
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
           "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"
+            "@types/yargs-parser": "*"
           }
         },
-        "nopt": {
-          "version": "4.0.1",
-          "bundled": 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,
-          "optional": true,
           "requires": {
-            "abbrev": "1",
-            "osenv": "^0.1.4"
+            "@types/color-name": "^1.1.1",
+            "color-convert": "^2.0.1"
           }
         },
-        "npm-bundled": {
-          "version": "1.0.6",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "npm-packlist": {
-          "version": "1.4.1",
-          "bundled": true,
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "ignore-walk": "^3.0.1",
-            "npm-bundled": "^1.0.1"
+            "ansi-styles": "^4.1.0",
+            "supports-color": "^7.1.0"
           }
         },
-        "npmlog": {
-          "version": "4.1.2",
-          "bundled": true,
+        "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,
-          "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"
+            "color-name": "~1.1.4"
           }
         },
-        "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"
-          }
+        "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
         },
-        "os-homedir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": 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
         },
-        "os-tmpdir": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
+        "jest-get-type": {
+          "version": "26.0.0",
+          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz",
+          "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==",
+          "dev": true
         },
-        "osenv": {
-          "version": "0.1.5",
-          "bundled": 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,
-          "optional": true,
           "requires": {
-            "os-homedir": "^1.0.0",
-            "os-tmpdir": "^1.0.0"
+            "has-flag": "^4.0.0"
           }
-        },
-        "path-is-absolute": {
-          "version": "1.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "process-nextick-args": {
+        }
+      }
+    },
+    "ext": {
+      "version": "1.4.0",
+      "resolved": "https://registry.npmjs.org/ext/-/ext-1.4.0.tgz",
+      "integrity": "sha512-Key5NIsUxdqKg3vIsdw9dSuXpPCQ297y6wBjL30edxwPgt2E44WcWBZey/ZvUc6sERLTxKdyCu4gZFmUbk1Q7A==",
+      "dev": true,
+      "requires": {
+        "type": "^2.0.0"
+      },
+      "dependencies": {
+        "type": {
           "version": "2.0.0",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "rc": {
-          "version": "1.2.8",
-          "bundled": true,
+          "resolved": "https://registry.npmjs.org/type/-/type-2.0.0.tgz",
+          "integrity": "sha512-KBt58xCHry4Cejnc2ISQAF7QY+ORngsWfxezO68+12hKV6lQY8P/psIkcbjeHWn7MqcgciWJyCCevFMJdIXpow==",
+          "dev": true
+        }
+      }
+    },
+    "extend": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz",
+      "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==",
+      "dev": true
+    },
+    "extend-shallow": {
+      "version": "3.0.2",
+      "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-3.0.2.tgz",
+      "integrity": "sha1-Jqcarwc7OfshJxcnRhMcJwQCjbg=",
+      "dev": true,
+      "requires": {
+        "assign-symbols": "^1.0.0",
+        "is-extendable": "^1.0.1"
+      },
+      "dependencies": {
+        "is-extendable": {
+          "version": "1.0.1",
+          "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-1.0.1.tgz",
+          "integrity": "sha512-arnXMxT1hhoKo9k1LZdmlNyJdDDfy2v0fXjFlmok4+i8ul/6WlbVge9bhM74OpNPQPMGUToDtz+KXa1PneJxOA==",
           "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
-            }
+            "is-plain-object": "^2.0.4"
           }
-        },
-        "readable-stream": {
-          "version": "2.3.6",
-          "bundled": true,
+        }
+      }
+    },
+    "external-editor": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/external-editor/-/external-editor-3.1.0.tgz",
+      "integrity": "sha512-hMQ4CX1p1izmuLYyZqLMO/qGNw10wSv9QDCPfzXfyFrOaCSSoRfqE1Kf1s5an66J5JZC62NewG+mK49jOCtQew==",
+      "dev": true,
+      "requires": {
+        "chardet": "^0.7.0",
+        "iconv-lite": "^0.4.24",
+        "tmp": "^0.0.33"
+      }
+    },
+    "extglob": {
+      "version": "2.0.4",
+      "resolved": "https://registry.npmjs.org/extglob/-/extglob-2.0.4.tgz",
+      "integrity": "sha512-Nmb6QXkELsuBr24CJSkilo6UHHgbekK5UiZgfE6UHD3Eb27YC6oD+bhcT+tJ6cl8dmsgdQxnWlcry8ksBIBLpw==",
+      "dev": true,
+      "requires": {
+        "array-unique": "^0.3.2",
+        "define-property": "^1.0.0",
+        "expand-brackets": "^2.1.4",
+        "extend-shallow": "^2.0.1",
+        "fragment-cache": "^0.2.1",
+        "regex-not": "^1.0.0",
+        "snapdragon": "^0.8.1",
+        "to-regex": "^3.0.1"
+      },
+      "dependencies": {
+        "define-property": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/define-property/-/define-property-1.0.0.tgz",
+          "integrity": "sha1-dp66rz9KY6rTr56NMEybvnm/sOY=",
           "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"
+            "is-descriptor": "^1.0.0"
           }
         },
-        "rimraf": {
-          "version": "2.6.3",
-          "bundled": true,
+        "extend-shallow": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/extend-shallow/-/extend-shallow-2.0.1.tgz",
+          "integrity": "sha1-Ua99YUrZqfYQ6huvu5idaxxWiQ8=",
           "dev": true,
-          "optional": true,
           "requires": {
-            "glob": "^7.1.3"
+            "is-extendable": "^0.1.0"
           }
         },
-        "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,
+        "is-accessor-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-accessor-descriptor/-/is-accessor-descriptor-1.0.0.tgz",
+          "integrity": "sha512-m5hnHTkcVsPfqx3AKlyttIPb7J+XykHvJP2B9bZDjlhLIoEq4XoK64Vg7boZlVWYK6LUY94dYPEE7Lh0ZkZKcQ==",
           "dev": true,
-          "optional": true
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
         },
-        "signal-exit": {
-          "version": "3.0.2",
-          "bundled": true,
+        "is-data-descriptor": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/is-data-descriptor/-/is-data-descriptor-1.0.0.tgz",
+          "integrity": "sha512-jbRXy1FmtAoCjQkVmIVYwuuqDFUbaOeDjmed1tOGPrsMhtJA4rD9tkgA0F1qJ3gRFRXcHYVkdeaP50Q5rE/jLQ==",
           "dev": true,
-          "optional": true
+          "requires": {
+            "kind-of": "^6.0.0"
+          }
         },
-        "string-width": {
+        "is-descriptor": {
           "version": "1.0.2",
-          "bundled": true,
+          "resolved": "https://registry.npmjs.org/is-descriptor/-/is-descriptor-1.0.2.tgz",
+          "integrity": "sha512-2eis5WqQGV7peooDyLmNEPUrps9+SXX5c9pL3xEB+4e9HnGuDa7mB7kHxHw4CbqS9k1T2hOH3miL8n8WtiYVtg==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "code-point-at": "^1.0.0",
-            "is-fullwidth-code-point": "^1.0.0",
-            "strip-ansi": "^3.0.0"
+            "is-accessor-descriptor": "^1.0.0",
+            "is-data-descriptor": "^1.0.0",
+            "kind-of": "^6.0.2"
           }
-        },
-        "string_decoder": {
-          "version": "1.1.1",
-          "bundled": true,
+        }
+      }
+    },
+    "extsprintf": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/extsprintf/-/extsprintf-1.3.0.tgz",
+      "integrity": "sha1-lpGEQOMEGnpBT4xS48V06zw+HgU=",
+      "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=",
+      "dev": true
+    },
+    "fast-diff": {
+      "version": "1.2.0",
+      "resolved": "https://registry.npmjs.org/fast-diff/-/fast-diff-1.2.0.tgz",
+      "integrity": "sha512-xJuoT5+L99XlZ8twedaRf6Ax2TgQVxvgZOYoPKqZufmJib0tL2tegPBOZb1pVNgIhlqDlA0eO0c3wBvQcmzx4w==",
+      "dev": true
+    },
+    "fast-glob": {
+      "version": "3.2.4",
+      "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.4.tgz",
+      "integrity": "sha512-kr/Oo6PX51265qeuCYsyGypiO5uJFgBS0jksyG7FUeCyQzNwYnzrNIMR1NXfkZXsMYXYLRAHgISHBz8gQcxKHQ==",
+      "dev": true,
+      "requires": {
+        "@nodelib/fs.stat": "^2.0.2",
+        "@nodelib/fs.walk": "^1.2.3",
+        "glob-parent": "^5.1.0",
+        "merge2": "^1.3.0",
+        "micromatch": "^4.0.2",
+        "picomatch": "^2.2.1"
+      },
+      "dependencies": {
+        "braces": {
+          "version": "3.0.2",
+          "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
+          "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
           "dev": true,
-          "optional": true,
           "requires": {
-            "safe-buffer": "~5.1.0"
+            "fill-range": "^7.0.1"
           }
         },
-        "strip-ansi": {
-          "version": "3.0.1",
-          "bundled": 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,
-          "optional": true,
           "requires": {
-            "ansi-regex": "^2.0.0"
+            "to-regex-range": "^5.0.1"
           }
         },
-        "strip-json-comments": {
-          "version": "2.0.1",
-          "bundled": true,
-          "dev": true,
-          "optional": 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
         },
-        "tar": {
-          "version": "4.4.8",
-          "bundled": 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,
-          "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"
+            "braces": "^3.0.1",
+            "picomatch": "^2.0.5"
           }
         },
-        "util-deprecate": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "wide-align": {
-          "version": "1.1.3",
-          "bundled": 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,
-          "optional": true,
           "requires": {
-            "string-width": "^1.0.2 || 2"
+            "is-number": "^7.0.0"
           }
-        },
-        "wrappy": {
-          "version": "1.0.2",
-          "bundled": true,
-          "dev": true,
-          "optional": true
-        },
-        "yallist": {
-          "version": "3.0.3",
-          "bundled": true,
-          "dev": true,
-          "optional": 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=",
+      "dev": true
+    },
+    "fast-levenshtein": {
+      "version": "2.0.6",
+      "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz",
+      "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=",
+      "dev": true
+    },
+    "fastq": {
+      "version": "1.8.0",
+      "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.8.0.tgz",
+      "integrity": "sha512-SMIZoZdLh/fgofivvIkmknUXyPnvxRE3DhtZ5Me3Mrsk5gyPL42F0xr51TdRXskBxHfMp+07bcYzfsYEsSQA9Q==",
+      "dev": true,
+      "requires": {
+        "reusify": "^1.0.4"
+      }
+    },
+    "fb-watchman": {
+      "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.1.1"
+      }
+    },
+    "figures": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/figures/-/figures-2.0.0.tgz",
+      "integrity": "sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=",
+      "dev": true,
+      "requires": {
+        "escape-string-regexp": "^1.0.5"
+      }
+    },
+    "file-entry-cache": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-5.0.1.tgz",
+      "integrity": "sha512-bCg29ictuBaKUwwArK4ouCaqDgLZcysCFLmM/Yn/FDoqndh/9vNuQfXRDvTuXKLxfD/JtZQGKFT8MGcJBK644g==",
+      "dev": true,
+      "requires": {
+        "flat-cache": "^2.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"
+      }
+    },
+    "find-up": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-2.1.0.tgz",
+      "integrity": "sha1-RdG35QbHF93UgndaK3eSCjwMV6c=",
+      "dev": true,
+      "requires": {
+        "locate-path": "^2.0.0"
+      }
+    },
+    "flat-cache": {
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-2.0.1.tgz",
+      "integrity": "sha512-LoQe6yDuUMDzQAEH8sgmh4Md6oZnc/7PjtwjNFSzveXqSHt6ka9fPBuso7IGf9Rz4uqnSnWiFH2B/zj24a5ReA==",
+      "dev": true,
+      "requires": {
+        "flatted": "^2.0.0",
+        "rimraf": "2.6.3",
+        "write": "1.0.3"
+      }
+    },
+    "flatted": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/flatted/-/flatted-2.0.2.tgz",
+      "integrity": "sha512-r5wGx7YeOwNWNlCA0wQ86zKyDLMQr+/RB8xy74M4hTphfmjlijTSSXGuH8rnvKZnfT9i+75zmd8jcKdMR4O6jA==",
+      "dev": true
+    },
+    "for-in": {
+      "version": "1.0.2",
+      "resolved": "https://registry.npmjs.org/for-in/-/for-in-1.0.2.tgz",
+      "integrity": "sha1-gQaNKVqBQuwKxybG4iAMMPttXoA=",
+      "dev": true
+    },
+    "forever-agent": {
+      "version": "0.6.1",
+      "resolved": "https://registry.npmjs.org/forever-agent/-/forever-agent-0.6.1.tgz",
+      "integrity": "sha1-+8cfDEGt6zf5bFd60e1C2P2sypE=",
+      "dev": true
+    },
+    "form-data": {
+      "version": "2.3.3",
+      "resolved": "https://registry.npmjs.org/form-data/-/form-data-2.3.3.tgz",
+      "integrity": "sha512-1lLKB2Mu3aGP1Q/2eCOx0fNbRMe7XdwktwOruhfqqd0rIJWwN4Dh+E3hrPSlDCXnSR7UtZ1N38rVXm+6+MEhJQ==",
+      "dev": true,
+      "requires": {
+        "asynckit": "^0.4.0",
+        "combined-stream": "^1.0.6",
+        "mime-types": "^2.1.12"
+      }
+    },
+    "fragment-cache": {
+      "version": "0.2.1",
+      "resolved": "https://registry.npmjs.org/fragment-cache/-/fragment-cache-0.2.1.tgz",
+      "integrity": "sha1-QpD60n8T6Jvn8zeZxrxaCr//DRk=",
+      "dev": true,
+      "requires": {
+        "map-cache": "^0.2.2"
+      }
+    },
+    "fs-extra": {
+      "version": "9.0.1",
+      "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-9.0.1.tgz",
+      "integrity": "sha512-h2iAoN838FqAFJY2/qVpzFXy+EBxfVE220PalAqQLDVsFOHLJrZvut5puAbCdNv6WJk+B8ihI+k0c7JK5erwqQ==",
+      "dev": true,
+      "requires": {
+        "at-least-node": "^1.0.0",
+        "graceful-fs": "^4.2.0",
+        "jsonfile": "^6.0.1",
+        "universalify": "^1.0.0"
+      }
+    },
+    "fs.realpath": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz",
+      "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8="
+    },
+    "fsevents": {
+      "version": "2.1.3",
+      "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.1.3.tgz",
+      "integrity": "sha512-Auw9a4AxqWpa9GUfj370BMPzzyncfBABW8Mab7BGWBYDj4Isgq+cDKtx0i6u9jcX9pQDnswsaaOTgTmA5pEjuQ==",
+      "dev": true,
+      "optional": true
+    },
     "function-bind": {
       "version": "1.1.1",
       "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.1.tgz",
@@ -3178,12 +4749,24 @@
       "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=",
       "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-package-type": {
+      "version": "0.1.0",
+      "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz",
+      "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
+      "dev": true
+    },
     "get-stdin": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/get-stdin/-/get-stdin-6.0.0.tgz",
@@ -3217,7 +4800,6 @@
       "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",
@@ -3227,12 +4809,49 @@
         "path-is-absolute": "^1.0.0"
       }
     },
+    "glob-parent": {
+      "version": "5.1.1",
+      "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.1.tgz",
+      "integrity": "sha512-FnI+VGOpnlGHWZxthPGR+QhR78fuiK0sNLkHQv+bL9fQi57lNNdquIbna/WrfROrolq8GK5Ek6BiMwqL/voRYQ==",
+      "dev": true,
+      "requires": {
+        "is-glob": "^4.0.1"
+      }
+    },
     "globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
       "integrity": "sha512-WOBp/EEGUiIsJSp7wcv/y6MO+lV9UoncWqxuFfm8eBwzWNgyfBd6Gz+IeKQ9jCmyhoH99g15M3T+QaVHFjizVA==",
       "dev": true
     },
+    "globby": {
+      "version": "11.0.1",
+      "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.1.tgz",
+      "integrity": "sha512-iH9RmgwCmUJHi2z5o2l3eTtGBtXek1OYlHrbcxOYugyHLmAsZrPj43OtHThd62Buh/Vv6VyCBD2bdyWcGNQqoQ==",
+      "dev": true,
+      "requires": {
+        "array-union": "^2.1.0",
+        "dir-glob": "^3.0.1",
+        "fast-glob": "^3.1.1",
+        "ignore": "^5.1.4",
+        "merge2": "^1.3.0",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "ignore": {
+          "version": "5.1.8",
+          "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz",
+          "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==",
+          "dev": true
+        },
+        "slash": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz",
+          "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
+          "dev": true
+        }
+      }
+    },
     "graceful-fs": {
       "version": "4.2.3",
       "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.3.tgz",
@@ -3249,62 +4868,37 @@
       }
     },
     "graphql-config": {
-      "version": "2.2.1",
-      "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-2.2.1.tgz",
-      "integrity": "sha512-U8+1IAhw9m6WkZRRcyj8ZarK96R6lQBQ0an4lp76Ps9FyhOXENC5YQOxOFGm5CxPrX2rD0g3Je4zG5xdNJjwzQ==",
-      "dev": true,
-      "requires": {
-        "graphql-import": "^0.7.1",
-        "graphql-request": "^1.5.0",
-        "js-yaml": "^3.10.0",
-        "lodash": "^4.17.4",
-        "minimatch": "^3.0.4"
-      }
-    },
-    "graphql-import": {
-      "version": "0.7.1",
-      "resolved": "https://registry.npmjs.org/graphql-import/-/graphql-import-0.7.1.tgz",
-      "integrity": "sha512-YpwpaPjRUVlw2SN3OPljpWbVRWAhMAyfSba5U47qGMOSsPLi2gYeJtngGpymjm9nk57RFWEpjqwh4+dpYuFAPw==",
-      "dev": true,
-      "requires": {
-        "lodash": "^4.17.4",
-        "resolve-from": "^4.0.0"
-      }
-    },
-    "graphql-request": {
-      "version": "1.8.2",
-      "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-1.8.2.tgz",
-      "integrity": "sha512-dDX2M+VMsxXFCmUX0Vo0TopIZIX4ggzOtiCsThgtrKR4niiaagsGTDIHj3fsOMFETpa064vzovI+4YV4QnMbcg==",
-      "dev": true,
-      "requires": {
-        "cross-fetch": "2.2.2"
+      "version": "3.0.3",
+      "resolved": "https://registry.npmjs.org/graphql-config/-/graphql-config-3.0.3.tgz",
+      "integrity": "sha512-MBY0wEjvcgJtZUyoqpPvOE1e5qPI0hJaa1gKTqjonSFiCsNHX2lykNjpOPcodmAgH1V06ELxhGnm9kcVzqvi/g==",
+      "dev": true,
+      "requires": {
+        "@graphql-tools/graphql-file-loader": "^6.0.0",
+        "@graphql-tools/json-file-loader": "^6.0.0",
+        "@graphql-tools/load": "^6.0.0",
+        "@graphql-tools/merge": "^6.0.0",
+        "@graphql-tools/url-loader": "^6.0.0",
+        "@graphql-tools/utils": "^6.0.0",
+        "cosmiconfig": "6.0.0",
+        "minimatch": "3.0.4",
+        "string-env-interpolation": "1.0.1",
+        "tslib": "^2.0.0"
+      },
+      "dependencies": {
+        "tslib": {
+          "version": "2.0.1",
+          "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.0.1.tgz",
+          "integrity": "sha512-SgIkNheinmEBgx1IUNirK0TUD4X9yjjBRTqqjggWCU3pUEqIk3/Uwl3yRixYKT6WjQuGiwDv4NomL3wqRCj+CQ==",
+          "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"
-      },
-      "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
-        }
-      }
+      "optional": true
     },
     "har-schema": {
       "version": "2.0.0",
@@ -3313,13 +4907,33 @@
       "dev": true
     },
     "har-validator": {
-      "version": "5.1.3",
-      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.3.tgz",
-      "integrity": "sha512-sNvOCzEQNr/qrvJgc3UG/kD4QtlHycrzwS+6mfTrrSq97BvaYcPZZI1ZSqGSPR73Cxn4LKTD4PttRwfU7jWq5g==",
+      "version": "5.1.5",
+      "resolved": "https://registry.npmjs.org/har-validator/-/har-validator-5.1.5.tgz",
+      "integrity": "sha512-nmT2T0lljbxdQZfspsno9hgrG3Uir6Ks5afism62poxqBM6sDnMEuPmzTq8XN0OEwqKLLdh1jQI3qyE66Nzb3w==",
       "dev": true,
       "requires": {
-        "ajv": "^6.5.5",
+        "ajv": "^6.12.3",
         "har-schema": "^2.0.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "6.12.3",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.3.tgz",
+          "integrity": "sha512-4K0cK3L1hsqk9xIb2z9vs/XU+PGJZ9PNpJRDS9YLzmNdX6jmVPfamLvTJr0aDAusnHyCHO6MjzlkAsgtqp9teA==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.1",
+            "fast-json-stable-stringify": "^2.0.0",
+            "json-schema-traverse": "^0.4.1",
+            "uri-js": "^4.2.2"
+          }
+        },
+        "fast-deep-equal": {
+          "version": "3.1.3",
+          "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz",
+          "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==",
+          "dev": true
+        }
       }
     },
     "has": {
@@ -3364,6 +4978,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",
@@ -3382,14 +5016,20 @@
       "dev": true
     },
     "html-encoding-sniffer": {
-      "version": "1.0.2",
-      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-1.0.2.tgz",
-      "integrity": "sha512-71lZziiDnsuabfdYiUeWdCVyKuqwWi23L8YeIgV9jSSZHCtb6wB1BKWooH7L3tn4/FuZJMVWyNaIDr4RGmaSYw==",
+      "version": "2.0.1",
+      "resolved": "https://registry.npmjs.org/html-encoding-sniffer/-/html-encoding-sniffer-2.0.1.tgz",
+      "integrity": "sha512-D5JbOMBIR/TVZkubHT+OyT2705QvogUW4IBn6nHd756OwieSF9aDYFj4dv6HHEVGYbHaLETa3WggZYWWMyy3ZQ==",
       "dev": true,
       "requires": {
-        "whatwg-encoding": "^1.0.1"
+        "whatwg-encoding": "^1.0.5"
       }
     },
+    "html-escaper": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz",
+      "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==",
+      "dev": true
+    },
     "http-signature": {
       "version": "1.2.0",
       "resolved": "https://registry.npmjs.org/http-signature/-/http-signature-1.2.0.tgz",
@@ -3401,6 +5041,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",
@@ -3426,51 +5072,68 @@
         "resolve-from": "^4.0.0"
       }
     },
+    "import-from": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/import-from/-/import-from-3.0.0.tgz",
+      "integrity": "sha512-CiuXOFFSzkU5x/CR0+z7T91Iht4CXgfCxVOFRhh2Zyhg5wOpWvvDLQUsWl+gcN+QscYBjez8hDCt85O7RLDttQ==",
+      "dev": true,
+      "requires": {
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "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
+        }
+      }
+    },
     "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"
       },
       "dependencies": {
         "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"
           }
         },
         "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"
           }
         },
         "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.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
           "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": {
@@ -3479,13 +5142,19 @@
           "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": "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"
           }
         }
       }
@@ -3496,11 +5165,16 @@
       "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=",
       "dev": true
     },
+    "indent-string": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/indent-string/-/indent-string-4.0.0.tgz",
+      "integrity": "sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==",
+      "dev": true
+    },
     "inflight": {
       "version": "1.0.6",
       "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz",
       "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=",
-      "dev": true,
       "requires": {
         "once": "^1.3.0",
         "wrappy": "1"
@@ -3509,8 +5183,7 @@
     "inherits": {
       "version": "2.0.4",
       "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz",
-      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==",
-      "dev": true
+      "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ=="
     },
     "inquirer": {
       "version": "6.5.2",
@@ -3544,14 +5217,11 @@
         }
       }
     },
-    "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"
-      }
+    "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",
@@ -3645,6 +5315,13 @@
         }
       }
     },
+    "is-docker": {
+      "version": "2.1.1",
+      "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-2.1.1.tgz",
+      "integrity": "sha512-ZOoqiXfEwtGknTiuDEy8pN2CfE3TxMHprvNer1mXiqwkOT77Rw3YVrUQ52EqAOU3QAWDQ+bQdx7HJzrv7LS2Hw==",
+      "dev": true,
+      "optional": true
+    },
     "is-extendable": {
       "version": "0.1.1",
       "resolved": "https://registry.npmjs.org/is-extendable/-/is-extendable-0.1.1.tgz",
@@ -3679,24 +5356,10 @@
       }
     },
     "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": "2.0.4",
@@ -3707,6 +5370,12 @@
         "isobject": "^3.0.1"
       }
     },
+    "is-potential-custom-element-name": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/is-potential-custom-element-name/-/is-potential-custom-element-name-1.0.0.tgz",
+      "integrity": "sha1-DFLlS8yjkbssSUsh6GJtczbG45c=",
+      "dev": true
+    },
     "is-promise": {
       "version": "2.1.0",
       "resolved": "https://registry.npmjs.org/is-promise/-/is-promise-2.1.0.tgz",
@@ -3749,10 +5418,14 @@
       "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.2.0",
+      "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-2.2.0.tgz",
+      "integrity": "sha512-fKzAra0rGJUUBwGBgNkHZuToZcn+TtXHpeCgmkMJMMYx1sQDYaCSyjJBSCa2nH1DGm7s3n1oBnohoVTBaN7Lww==",
+      "dev": true,
+      "optional": true,
+      "requires": {
+        "is-docker": "^2.0.0"
+      }
     },
     "isarray": {
       "version": "1.0.0",
@@ -3778,58 +5451,59 @@
       "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.3",
+      "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-4.0.3.tgz",
+      "integrity": "sha512-BXgQl9kf4WTCPCCpmFGoJkz/+uhvm7h7PFKUYxh7qarQd3ER33vHG//qaE8eN25l07YqZPpHXU9I09l/RD5aGQ==",
       "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",
+        "@istanbuljs/schema": "^0.1.2",
+        "istanbul-lib-coverage": "^3.0.0",
+        "semver": "^6.3.0"
       }
     },
     "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
+        },
         "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": {
@@ -3842,12 +5516,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.2",
+      "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.0.2.tgz",
+      "integrity": "sha512-9tZvz7AiR3PEDNGiV9vIouQ/EAcqMXFmkcA1CDFTwOB98OZVDL0PH9glHotf5Ugp6GCOTypfzGWI/OqjWNCRUw==",
       "dev": true,
       "requires": {
-        "handlebars": "^4.1.2"
+        "html-escaper": "^2.0.0",
+        "istanbul-lib-report": "^3.0.0"
       }
     },
     "iterall": {
@@ -3857,96 +5532,547 @@
       "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": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest/-/jest-26.2.2.tgz",
+      "integrity": "sha512-EkJNyHiAG1+A8pqSz7cXttoVa34hOEzN/MrnJhYnfp5VHxflVcf2pu3oJSrhiy6LfIutLdWo+n6q63tjcoIeig==",
       "dev": true,
       "requires": {
-        "import-local": "^2.0.0",
-        "jest-cli": "^24.9.0"
+        "@jest/core": "^26.2.2",
+        "import-local": "^3.0.2",
+        "jest-cli": "^26.2.2"
       },
       "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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-cli": {
-          "version": "24.9.0",
-          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-24.9.0.tgz",
-          "integrity": "sha512-+VLRKyitT3BWoMeSUIHRxV/2g8y9gw91Jh5z2UmXZzkZKpbC08CSehVxgHUwTpy+HwGcns/tqafQDJW7imYvGg==",
+          "version": "26.2.2",
+          "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-26.2.2.tgz",
+          "integrity": "sha512-vVcly0n/ijZvdy6gPQiQt0YANwX2hLTPQZHtW7Vi3gcFdKTtif7YpI85F8R8JYy5DFSWz4x1OW0arnxlziu5Lw==",
           "dev": true,
           "requires": {
-            "@jest/core": "^24.9.0",
-            "@jest/test-result": "^24.9.0",
-            "@jest/types": "^24.9.0",
-            "chalk": "^2.0.1",
+            "@jest/core": "^26.2.2",
+            "@jest/test-result": "^26.2.0",
+            "@jest/types": "^26.2.0",
+            "chalk": "^4.0.0",
             "exit": "^0.1.2",
-            "import-local": "^2.0.0",
+            "graceful-fs": "^4.2.4",
+            "import-local": "^3.0.2",
             "is-ci": "^2.0.0",
-            "jest-config": "^24.9.0",
-            "jest-util": "^24.9.0",
-            "jest-validate": "^24.9.0",
+            "jest-config": "^26.2.2",
+            "jest-util": "^26.2.0",
+            "jest-validate": "^26.2.0",
             "prompts": "^2.0.1",
-            "realpath-native": "^1.1.0",
-            "yargs": "^13.3.0"
+            "yargs": "^15.3.1"
+          }
+        },
+        "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-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==",
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-26.2.0.tgz",
+      "integrity": "sha512-+RyJb+F1K/XBLIYiL449vo5D+CvlHv29QveJUWNPXuUicyZcq+tf1wNxmmFeRvAU1+TzhwqczSjxnCCFt7+8iA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^24.9.0",
-        "execa": "^1.0.0",
-        "throat": "^4.0.0"
+        "@jest/types": "^26.2.0",
+        "execa": "^4.0.0",
+        "throat": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "cross-spawn": {
+          "version": "7.0.3",
+          "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz",
+          "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==",
+          "dev": true,
+          "requires": {
+            "path-key": "^3.1.0",
+            "shebang-command": "^2.0.0",
+            "which": "^2.0.1"
+          }
+        },
+        "execa": {
+          "version": "4.0.3",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-4.0.3.tgz",
+          "integrity": "sha512-WFDXGHckXPWZX19t1kCsXzOpqX9LWYNqn4C+HqZlk/V0imTkzJZqf87ZBhvpHaftERYknpk0fjSylnXVlVgI0A==",
+          "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",
+            "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"
+          }
+        },
+        "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
+        },
+        "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": {
+            "path-key": "^3.0.0"
+          }
+        },
+        "onetime": {
+          "version": "5.1.1",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.1.tgz",
+          "integrity": "sha512-ZpZpjcJeugQfWsfyQlshVoowIIQ1qBGSVll4rfDq6JJVO//fesjoX808hXWfBjY+ROZgpKDI5TRSRBSoJiZ8eg==",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^2.1.0"
+          }
+        },
+        "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
+        },
+        "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"
+          }
+        },
+        "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-circus": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-24.9.0.tgz",
-      "integrity": "sha512-dwkvwFtRc9Anmk1XTc+bonVL8rVMZ3CeGMoFWmv1oaQThdAgvfI9bwaFlZp+gLVphNVz6ZLfCWo3ERhS5CeVvA==",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-26.2.2.tgz",
+      "integrity": "sha512-o8yaf3atbWED1dfM/Vxl7VsgN/tDskh5ptqsTF/oCCLPo5RVV39Ubr5/Y9fdW/mP/gNQe+QsrqcHKXHdK/00GQ==",
       "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",
+        "@jest/environment": "^26.2.0",
+        "@jest/test-result": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
         "co": "^4.6.0",
-        "expect": "^24.9.0",
+        "dedent": "^0.7.0",
+        "expect": "^26.2.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-snapshot": "^24.9.0",
-        "jest-util": "^24.9.0",
-        "pretty-format": "^24.9.0",
-        "stack-utils": "^1.0.1",
-        "throat": "^4.0.0"
+        "jest-each": "^26.2.0",
+        "jest-matcher-utils": "^26.2.0",
+        "jest-message-util": "^26.2.0",
+        "jest-runner": "^26.2.2",
+        "jest-runtime": "^26.2.2",
+        "jest-snapshot": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "pretty-format": "^26.2.0",
+        "stack-utils": "^2.0.2",
+        "throat": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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-config": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-24.9.0.tgz",
-      "integrity": "sha512-RATtQJtVYQrp7fvWg6f5y3pEFj9I+H8sWw4aKxnDZ96mob5i5SD6ZEGWgMLXQ4LE8UurrjbdlLWdUeo+28QpfQ==",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-26.2.2.tgz",
+      "integrity": "sha512-2lhxH0y4YFOijMJ65usuf78m7+9/8+hAb1PZQtdRdgnQpAb4zP6KcVDDktpHEkspBKnc2lmFu+RQdHukUUbiTg==",
       "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",
+        "@jest/test-sequencer": "^26.2.2",
+        "@jest/types": "^26.2.0",
+        "babel-jest": "^26.2.2",
+        "chalk": "^4.0.0",
+        "deepmerge": "^4.2.2",
         "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"
+        "graceful-fs": "^4.2.4",
+        "jest-environment-jsdom": "^26.2.0",
+        "jest-environment-node": "^26.2.0",
+        "jest-get-type": "^26.0.0",
+        "jest-jasmine2": "^26.2.2",
+        "jest-regex-util": "^26.0.0",
+        "jest-resolve": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "jest-validate": "^26.2.0",
+        "micromatch": "^4.0.2",
+        "pretty-format": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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": "26.0.0",
+          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz",
+          "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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-diff": {
@@ -3962,52 +6088,300 @@
       }
     },
     "jest-docblock": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-24.9.0.tgz",
-      "integrity": "sha512-F1DjdpDMJMA1cN6He0FNYNZlo3yYmOtRUnktrT9Q37njYzC5WEaDdmbynIgy0L/IvXvvgsG8OsqhLPXTpfmZAA==",
+      "version": "26.0.0",
+      "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-26.0.0.tgz",
+      "integrity": "sha512-RDZ4Iz3QbtRWycd8bUEPxQsTlYazfYn/h5R65Fc6gOfwozFhoImx+affzky/FFBuqISPTqjXomoIGJVKBWoo0w==",
       "dev": true,
       "requires": {
-        "detect-newline": "^2.1.0"
+        "detect-newline": "^3.0.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==",
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-26.2.0.tgz",
+      "integrity": "sha512-gHPCaho1twWHB5bpcfnozlc6mrMi+VAewVPNgmwf81x2Gzr6XO4dl+eOrwPWxbkYlgjgrYjWK2xgKnixbzH3Ew==",
       "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/types": "^26.2.0",
+        "chalk": "^4.0.0",
+        "jest-get-type": "^26.0.0",
+        "jest-util": "^26.2.0",
+        "pretty-format": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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": "26.0.0",
+          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz",
+          "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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-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"
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-jsdom/-/jest-environment-jsdom-26.2.0.tgz",
+      "integrity": "sha512-sDG24+5M4NuIGzkI3rJW8XUlrpkvIdE9Zz4jhD8OBnVxAw+Y1jUk9X+lAOD48nlfUTlnt3lbAI3k2Ox+WF3S0g==",
+      "dev": true,
+      "requires": {
+        "@jest/environment": "^26.2.0",
+        "@jest/fake-timers": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "jest-mock": "^26.2.0",
+        "jest-util": "^26.2.0",
+        "jsdom": "^16.2.2"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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-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==",
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-26.2.0.tgz",
+      "integrity": "sha512-4M5ExTYkJ19efBzkiXtBi74JqKLDciEk4CEsp5tTjWGYMrlKFQFtwIVG3tW1OGE0AlXhZjuHPwubuRYY4j4uOw==",
       "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/environment": "^26.2.0",
+        "@jest/fake-timers": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "jest-mock": "^26.2.0",
+        "jest-util": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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-get-type": {
@@ -4017,291 +6391,1672 @@
       "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": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-26.2.2.tgz",
+      "integrity": "sha512-3sJlMSt+NHnzCB+0KhJ1Ut4zKJBiJOlbrqEYNdRQGlXTv8kqzZWjUKQRY3pkjmlf+7rYjAV++MQ4D6g4DhAyOg==",
       "dev": true,
       "requires": {
-        "@jest/types": "^24.9.0",
-        "anymatch": "^2.0.0",
+        "@jest/types": "^26.2.0",
+        "@types/graceful-fs": "^4.1.2",
+        "@types/node": "*",
+        "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.4",
+        "jest-regex-util": "^26.0.0",
+        "jest-serializer": "^26.2.0",
+        "jest-util": "^26.2.0",
+        "jest-worker": "^26.2.1",
+        "micromatch": "^4.0.2",
         "sane": "^4.0.3",
         "walker": "^1.0.7"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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-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==",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-jasmine2/-/jest-jasmine2-26.2.2.tgz",
+      "integrity": "sha512-Q8AAHpbiZMVMy4Hz9j1j1bg2yUmPa1W9StBvcHqRaKa9PHaDUMwds8LwaDyzP/2fkybcTQE4+pTMDOG9826tEw==",
       "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",
+        "@jest/environment": "^26.2.0",
+        "@jest/source-map": "^26.1.0",
+        "@jest/test-result": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
         "co": "^4.6.0",
-        "expect": "^24.9.0",
+        "expect": "^26.2.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"
+        "jest-each": "^26.2.0",
+        "jest-matcher-utils": "^26.2.0",
+        "jest-message-util": "^26.2.0",
+        "jest-runtime": "^26.2.2",
+        "jest-snapshot": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "pretty-format": "^26.2.0",
+        "throat": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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-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==",
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-26.2.0.tgz",
+      "integrity": "sha512-aQdzTX1YiufkXA1teXZu5xXOJgy7wZQw6OJ0iH5CtQlOETe6gTSocaYKUNui1SzQ91xmqEUZ/WRavg9FD82rtQ==",
       "dev": true,
       "requires": {
-        "jest-get-type": "^24.9.0",
-        "pretty-format": "^24.9.0"
+        "jest-get-type": "^26.0.0",
+        "pretty-format": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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": "26.0.0",
+          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz",
+          "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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.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": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-26.2.0.tgz",
+      "integrity": "sha512-2cf/LW2VFb3ayPHrH36ZDjp9+CAeAe/pWBAwsV8t3dKcrINzXPVxq8qMWOxwt5BaeBCx4ZupVGH7VIgB8v66vQ==",
       "dev": true,
       "requires": {
-        "chalk": "^2.0.1",
-        "jest-diff": "^24.9.0",
-        "jest-get-type": "^24.9.0",
-        "pretty-format": "^24.9.0"
+        "chalk": "^4.0.0",
+        "jest-diff": "^26.2.0",
+        "jest-get-type": "^26.0.0",
+        "pretty-format": "^26.2.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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": "26.0.0",
+          "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-26.0.0.tgz",
+          "integrity": "sha512-JC/eHYEC3aSS0vZGjuoc4vHA0yAQTzhQQldXMeMF+JlxLGJlCO38Gma82NV9gk1jGFz8mDzUMeaKXvjRRdJ2dg==",
+          "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-diff": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-26.2.0.tgz",
+          "integrity": "sha512-Wu4Aopi2nzCsHWLBlD48TgRy3Z7OsxlwvHNd1YSnHc7q1NJfrmyCPoUXrTIrydQOG5ApaYpsAsdfnMbJqV1/wQ==",
+          "dev": true,
+          "requires": {
+            "chalk": "^4.0.0",
+            "diff-sequences": "^26.0.0",
+            "jest-get-type": "^26.0.0",
+            "pretty-format": "^26.2.0"
+          }
+        },
+        "jest-get-type": {
+          "version": "26.0.0",
+          "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-26.0.0.tgz",
+          "integrity": "sha512-zRc1OAPnnws1EVfykXOj19zo2EMw5Hi6HLbFCSjpuJiXtOWAYIjNsHVSbpQ8bDX7L5BGYGI8m+HmKdjHYFF0kg==",
+          "dev": true
+        },
+        "pretty-format": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.2.0.tgz",
+          "integrity": "sha512-qi/8IuBu2clY9G7qCXgCdD1Bf9w+sXakdHTRToknzMtVy0g7c4MBWaZy7MfB7ndKZovRO6XRwJiAYqq+MC7SDA==",
+          "dev": true,
+          "requires": {
+            "@jest/types": "^26.2.0",
+            "ansi-regex": "^5.0.0",
+            "ansi-styles": "^4.0.0",
+            "react-is": "^16.12.0"
+          }
+        },
+        "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-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": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-26.2.0.tgz",
+      "integrity": "sha512-g362RhZaJuqeqG108n1sthz5vNpzTNy926eNDszo4ncRbmmcMRIUAZibnd6s5v2XSBCChAxQtCoN25gnzp7JbQ==",
       "dev": true,
       "requires": {
         "@babel/code-frame": "^7.0.0",
-        "@jest/test-result": "^24.9.0",
-        "@jest/types": "^24.9.0",
+        "@jest/types": "^26.2.0",
         "@types/stack-utils": "^1.0.1",
-        "chalk": "^2.0.1",
-        "micromatch": "^3.1.10",
-        "slash": "^2.0.0",
-        "stack-utils": "^1.0.1"
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.4",
+        "micromatch": "^4.0.2",
+        "slash": "^3.0.0",
+        "stack-utils": "^2.0.2"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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-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": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-26.2.0.tgz",
+      "integrity": "sha512-XeC7yWtWmWByoyVOHSsE7NYsbXJLtJNgmhD7z4MKumKm6ET0si81bsSLbQ64L5saK3TgsHo2B/UqG5KNZ1Sp/Q==",
       "dev": true,
       "requires": {
-        "@jest/types": "^24.9.0"
+        "@jest/types": "^26.2.0",
+        "@types/node": "*"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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-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==",
+      "version": "1.2.2",
+      "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.2.tgz",
+      "integrity": "sha512-olV41bKSMm8BdnuMsewT4jqlZ8+3TCARAXjZGT9jcoSnrfUnRCqnMoF9XEeoWjbzObpqF9dRhHQj0Xb9QdF6/w==",
       "dev": true
     },
     "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": "26.0.0",
+      "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-26.0.0.tgz",
+      "integrity": "sha512-Gv3ZIs/nA48/Zvjrl34bf+oD76JHiGDUxNOVgUjh3j890sblXryjY4rss71fPtD/njchl6PSE2hIhvyWa1eT0A==",
       "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"
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-26.2.2.tgz",
+      "integrity": "sha512-ye9Tj/ILn/0OgFPE/3dGpQPUqt4dHwIocxt5qSBkyzxQD8PbL0bVxBogX2FHxsd3zJA7V2H/cHXnBnNyyT9YoQ==",
+      "dev": true,
+      "requires": {
+        "@jest/types": "^26.2.0",
+        "chalk": "^4.0.0",
+        "graceful-fs": "^4.2.4",
+        "jest-pnp-resolver": "^1.2.2",
+        "jest-util": "^26.2.0",
+        "read-pkg-up": "^7.0.1",
+        "resolve": "^1.17.0",
+        "slash": "^3.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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"
+          }
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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.3.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz",
+          "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==",
+          "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
+        },
+        "parse-json": {
+          "version": "5.0.1",
+          "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.0.1.tgz",
+          "integrity": "sha512-ztoZ4/DYeXQq4E21v169sC8qWINGpcosGv9XhTDvg9/hWvx/zrFkc9BiWxR58OJLHGk28j5BL0SDLeV2WmFZlQ==",
+          "dev": true,
+          "requires": {
+            "@babel/code-frame": "^7.0.0",
+            "error-ex": "^1.3.1",
+            "json-parse-better-errors": "^1.0.1",
+            "lines-and-columns": "^1.1.6"
+          }
+        },
+        "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
+        },
+        "read-pkg": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-5.2.0.tgz",
+          "integrity": "sha512-Ug69mNOpfvKDAc2Q8DRpMjjzdtrnv9HcSMX+4VsZxD1aZ6ZzrIE7rlzXBtWTyhULSMKg076AW6WR5iZpD0JiOg==",
+          "dev": true,
+          "requires": {
+            "@types/normalize-package-data": "^2.4.0",
+            "normalize-package-data": "^2.5.0",
+            "parse-json": "^5.0.0",
+            "type-fest": "^0.6.0"
+          },
+          "dependencies": {
+            "type-fest": {
+              "version": "0.6.0",
+              "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.6.0.tgz",
+              "integrity": "sha512-q+MB8nYR1KDLrgr4G5yemftpMC7/QLqVndBmEEdqzmNj5dcFOO4Oo8qlwZE3ULT3+Zim1F8Kq4cBnikNhlCMlg==",
+              "dev": true
+            }
+          }
+        },
+        "read-pkg-up": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/read-pkg-up/-/read-pkg-up-7.0.1.tgz",
+          "integrity": "sha512-zK0TB7Xd6JpCLmlLmufqykGE+/TlOePD6qKClNW7hHDKFh/J7/7gCWGR7joEQEW1bKq3a3yUZSObOoWLFQ4ohg==",
+          "dev": true,
+          "requires": {
+            "find-up": "^4.1.0",
+            "read-pkg": "^5.2.0",
+            "type-fest": "^0.8.1"
+          }
+        },
+        "resolve": {
+          "version": "1.17.0",
+          "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.17.0.tgz",
+          "integrity": "sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==",
+          "dev": true,
+          "requires": {
+            "path-parse": "^1.0.6"
+          }
+        },
+        "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-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==",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-26.2.2.tgz",
+      "integrity": "sha512-S5vufDmVbQXnpP7435gr710xeBGUFcKNpNswke7RmFvDQtmqPjPVU/rCeMlEU0p6vfpnjhwMYeaVjKZAy5QYJA==",
       "dev": true,
       "requires": {
-        "@jest/types": "^24.9.0",
-        "jest-regex-util": "^24.3.0",
-        "jest-snapshot": "^24.9.0"
+        "@jest/types": "^26.2.0",
+        "jest-regex-util": "^26.0.0",
+        "jest-snapshot": "^26.2.2"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "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-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",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-26.2.2.tgz",
+      "integrity": "sha512-/qb6ptgX+KQ+aNMohJf1We695kaAfuu3u3ouh66TWfhTpLd9WbqcF6163d/tMoEY8GqPztXPLuyG0rHRVDLxCA==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^26.2.0",
+        "@jest/environment": "^26.2.0",
+        "@jest/test-result": "^26.2.0",
+        "@jest/types": "^26.2.0",
+        "@types/node": "*",
+        "chalk": "^4.0.0",
+        "emittery": "^0.7.1",
         "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",
+        "graceful-fs": "^4.2.4",
+        "jest-config": "^26.2.2",
+        "jest-docblock": "^26.0.0",
+        "jest-haste-map": "^26.2.2",
+        "jest-leak-detector": "^26.2.0",
+        "jest-message-util": "^26.2.0",
+        "jest-resolve": "^26.2.2",
+        "jest-runtime": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "jest-worker": "^26.2.1",
         "source-map-support": "^0.5.6",
-        "throat": "^4.0.0"
+        "throat": "^5.0.0"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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-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",
+      "version": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-26.2.2.tgz",
+      "integrity": "sha512-a8VXM3DxCDnCIdl9+QucWFfQ28KdqmyVFqeKLigHdErtsx56O2ZIdQkhFSuP1XtVrG9nTNHbKxjh5XL1UaFDVQ==",
+      "dev": true,
+      "requires": {
+        "@jest/console": "^26.2.0",
+        "@jest/environment": "^26.2.0",
+        "@jest/fake-timers": "^26.2.0",
+        "@jest/globals": "^26.2.0",
+        "@jest/source-map": "^26.1.0",
+        "@jest/test-result": "^26.2.0",
+        "@jest/transform": "^26.2.2",
+        "@jest/types": "^26.2.0",
+        "@types/yargs": "^15.0.0",
+        "chalk": "^4.0.0",
+        "collect-v8-coverage": "^1.0.0",
         "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"
+        "graceful-fs": "^4.2.4",
+        "jest-config": "^26.2.2",
+        "jest-haste-map": "^26.2.2",
+        "jest-message-util": "^26.2.0",
+        "jest-mock": "^26.2.0",
+        "jest-regex-util": "^26.0.0",
+        "jest-resolve": "^26.2.2",
+        "jest-snapshot": "^26.2.2",
+        "jest-util": "^26.2.0",
+        "jest-validate": "^26.2.0",
+        "slash": "^3.0.0",
+        "strip-bom": "^4.0.0",
+        "yargs": "^15.3.1"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "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"
+          }
+        },
+        "chalk": {
+          "version": "4.1.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.0.tgz",
+          "integrity": "sha512-qwx12AxXe2Q5xQ43Ac//I6v5aXTipYrSESdOgzrN+9XjgEpyjpKuvSGaN4qE93f7TQTlerQQ8S+EQ0EyDoVL1A==",
+          "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
+        },
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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
+        },
+        "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": "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-serializer": {
-      "version": "24.9.0",
-      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-24.9.0.tgz",
-      "integrity": "sha512-DxYipDr8OvfrKH3Kel6NdED3OXxjvxXZ1uIY2I9OFbGg+vUkkg7AGvi65qbhbWNPvDckXmzMPbK3u3HaDO49bQ==",
-      "dev": true
+      "version": "26.2.0",
+      "resolved": "https://registry.npmjs.org/jest-serializer/-/jest-serializer-26.2.0.tgz",
+      "integrity": "sha512-V7snZI9IVmyJEu0Qy0inmuXgnMWDtrsbV2p9CRAcmlmPVwpC2ZM8wXyYpiugDQnwLHx0V4+Pnog9Exb3UO8M6Q==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*",
+        "graceful-fs": "^4.2.4"
+      },
+      "dependencies": {
+        "graceful-fs": {
+          "version": "4.2.4",
+          "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.4.tgz",
+          "integrity": "sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==",
+          "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": "26.2.2",
+      "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-26.2.2.tgz",
+      "integrity": "sha512-NdjD8aJS7ePu268Wy/n/aR1TUisG0BOY+QOW4f6h46UHEKOgYmmkvJhh2BqdVZQ0BHSxTMt04WpCf9njzx8KtA==",
       "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",
+        "@jest/types": "^26.2.0",
+        "@types/prettier": "^2.0.0",
+        "chalk": "^4.0.0",
+        "expect": "^26.2.0",
+        "graceful-fs": "^4.2.4",
+        "jest-diff": "^26.2.0",
+        "jest-get-type": "^26.0.0",
+        "jest-haste-map": "^26.2.2",
+        "jest-matcher-utils": "^26.2.0",
+        "jest-message-util": "^26.2.0",
+        "jest-resolve": "^26.2.2",
         "natural-compare": "^1.4.0",
-        "pretty-format": "^24.9.0",
-        "semver": "^6.2.0"
+        "pretty-format": "^26.2.0",
+        "semver": "^7.3.2"
+      },
+      "dependencies": {
+        "@jest/types": {
+          "version": "26.2.0",
+          "resolved": "https://registry.npmjs.org/@jest/types/-/types-26.2.0.tgz",
+          "integrity": "sha512-lvm3rJvctxd7+wxKSxxbzpDbr4FXDLaC57WEKdUIZ2cjTYuxYSc0zlyD7Z4Uqr5VdKxRUrtwIkiqBuvgf8uKJA==",
+          "dev": true,
+          "requires": {
+            "@types/istanbul-lib-coverage": "^2.0.0",
+            "@types/istanbul-reports": "^1.1.1",
+            "@types/node": "*",
+            "@types/yargs": "^15.0.0",
+            "chalk": "^4.0.0"
+          }
+        },
+        "@types/yargs": {
+          "version": "15.0.5",
+          "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-15.0.5.tgz",
+          "integrity": "sha512-Dk/IDOPtOgubt/IaevIUbTgV7doaKkoorvOyYM2CMwuDyP89bekI7H4xLIwunNYiK9jhCkmc6pUrJk3cj2AB9w==",
+          "dev": true,
... 3240 lines suppressed ...


[airflow-cancel-workflow-runs] 25/44: Merge pull request #5 from jamesnetherton/fixup-warnings

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 2e3c1893986568a2197c41957b9c809cbcf1a61e
Merge: de231f5 6cc4a33
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Thu May 21 02:23:05 2020 -0500

    Merge pull request #5 from jamesnetherton/fixup-warnings
    
    Add workflow parameter to inputs list

 README.md   | 2 +-
 action.yml  | 3 +++
 src/main.ts | 2 +-
 3 files changed, 5 insertions(+), 2 deletions(-)


[airflow-cancel-workflow-runs] 19/44: Update README.md

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 0180a5b4d9039da27ff05fae7301675e80b41b1d
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Sun Mar 1 19:28:57 2020 -0600

    Update README.md
---
 README.md | 3 +--
 1 file changed, 1 insertion(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 4cb4e7c..d25c46f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,5 @@
 # cancel-previous-runs 
-
-[**BETA**] This action cancels previous runs for one or more branches/prs associated with a workflow, effectively limiting the resource consumption of the workflow to one per branch.
+This action cancels previous runs for one or more branches/prs associated with a workflow, effectively limiting the resource consumption of the workflow to one per branch.
 
 <p><a href="https://github.com/actions/typescript-action/actions"><img alt="typescript-action status" src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
 


[airflow-cancel-workflow-runs] 06/44: Update README.md

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit e41960ef5c41bce5c50c1382faf2743a394b4b2e
Author: Jason T. Greene <ja...@stacksmash.com>
AuthorDate: Tue Feb 4 09:41:14 2020 -0600

    Update README.md
---
 README.md | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/README.md b/README.md
index 22f77f6..76a856f 100644
--- a/README.md
+++ b/README.md
@@ -1,6 +1,6 @@
 # cancel-previous-runs 
 
-This action cancels all previous runs on the same branch, effectively limiting the resource consumption of the workflow using this action to one run per branch. 
+[**BETA**] This action cancels all previous runs on the same branch, effectively limiting the resource consumption of the workflow using this action to one run per branch. 
 
 <p><a href="https://github.com/actions/typescript-action/actions"><img alt="typescript-action status" src="https://github.com/actions/typescript-action/workflows/build-test/badge.svg"></a>
 


[airflow-cancel-workflow-runs] 17/44: spelling

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 888f33bf9b993bcd85b0910043e90965cc687e2c
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Wed Feb 19 15:12:56 2020 -0600

    spelling
---
 README.md | 4 ++--
 1 file changed, 2 insertions(+), 2 deletions(-)

diff --git a/README.md b/README.md
index 056b04c..4cb4e7c 100644
--- a/README.md
+++ b/README.md
@@ -8,11 +8,11 @@
 
 The easiest and most complete approach to utilize this action, is to create a separate schedule event triggered workflow, which is directed at the workflow you wish to clear duplicate runs. At each cron interrval all branches and all PRs executing for either push or pull_request events will be processed and limited to one run per branch/pr. 
 
-Additionally this action can be placed as an early step in your workflow (e.g. after chekout), so that it can abort the other previously running jobs immediatily, in case most resources are tied up. Unfortunately this approach is a no-op wheen a pull request uses a fork for a source branch. This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only peermissions for security reasons. write permissions are required to be able to cancel a job. Therefore, [...]
+Additionally this action can be placed as an early step in your workflow (e.g. after checkout), so that it can abort the other previously running jobs immediately, in case most resources are tied up. Unfortunately this approach is a no-op when a pull request uses a fork for a source branch. This is because the GITHUB_TOKEN provided to runs with a fork source branch specifies reed-only permissions for security reasons. write permissions are required to be able to cancel a job. Therefore,  [...]
 
 ### Inputs
 
-token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible in the repository, **DO NOT HARDODE A TOKEN ONLY USE A REFERENCE**. 
+token - The github token passed from `${{ secrets.GITHUB_TOKEN }}`. Since workflow files are visible in the repository, **DO NOT HARDCODE A TOKEN ONLY USE A REFERENCE**. 
 workflow - The filename of the workflow to limit runs on (only applies to schedule events) 
 
 


[airflow-cancel-workflow-runs] 40/44: Adds allDuplicatedNamedJobs mode (#11)

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 19f8bd20fd32b5b1d7857036b98e62550a442616
Author: Jarek Potiuk <ja...@potiuk.com>
AuthorDate: Sun Nov 1 13:58:15 2020 +0100

    Adds allDuplicatedNamedJobs mode (#11)
---
 README.md     |  84 +++++--
 dist/index.js | 459 +++++++++++++++++++++-----------------
 src/main.ts   | 707 +++++++++++++++++++++++++++++++++-------------------------
 3 files changed, 711 insertions(+), 539 deletions(-)

diff --git a/README.md b/README.md
index e81c66b..b212dd9 100644
--- a/README.md
+++ b/README.md
@@ -25,6 +25,7 @@
     - [Fail-fast source workflow runs with failed jobs and corresponding triggered runs](#fail-fast-source-workflow-runs-with-failed-jobs-and-corresponding-triggered-runs)
     - [Fail-fast for triggered workflow runs with failed jobs](#fail-fast-for-triggered-workflow-runs-with-failed-jobs)
     - [Cancel another workflow run](#cancel-another-workflow-run)
+    - [Cancel all duplicates for named jobs](#cancel-all-duplicates-for-named-jobs)
   - [Repositories that do not use Pull Requests from forks](#repositories-that-do-not-use-pull-requests-from-forks)
     - [Cancel duplicate runs for "self" workflow](#cancel-duplicate-runs-for-self-workflow)
     - [Cancel "self" workflow run](#cancel-self-workflow-run)
@@ -112,20 +113,21 @@ If you want a comprehensive solution, you should use the action as follows:
 | `notifyPRCancel`         | no       |              | Boolean. If set to true, it notifies the cancelled PRs with a comment containing reason why they are being cancelled.                                                                                            |
 | `notifyPRCancelMessage`  | no       |              | Optional cancel message to use instead of the default one when notifyPRCancel is true.  It is only used in 'self' cancelling mode.                                                                               |
 | `notifyPRMessageStart`   | no       |              | Only for workflow_run events triggered by the PRs. If not empty, it notifies those PRs with the message specified at the start of the workflow - adding the link to the triggered workflow_run.                  |
-| `jobNameRegexps`         | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` cancel modes.                          |
+| `jobNameRegexps`         | no       |              | An array of job name regexps. Only runs containing any job name matching any of of the regexp in this array are considered for cancelling in `failedJobs` and `namedJobs` and `allDuplicateNamedJobs` modes.     |
 | `skipEventTypes`         | no       |              | Array of event names that should be skipped when cancelling (JSON-encoded string). This might be used in order to skip direct pushes or scheduled events.                                                        |
 | `workflowFileName`       | no       |              | Name of the workflow file. It can be used if you want to cancel a different workflow than yours.                                                                                                                 |
 
 
 The job cancel modes work as follows:
 
-| Cancel Mode     | No `sourceRunId` specified                                             | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                      |
-|-----------------|------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
-| `duplicates`    | Cancels duplicate runs from the same repo/branch as current run.       | Cancels duplicate runs for the same repo/branch as the source run.                  |
-| `allDuplicates` | Cancels duplicate runs from all running workflows.                     | Cancels duplicate runs from all running workflows.                                  |
-| `self`          | Cancels self run.                                                      | Cancel the `sourceRunId` run.                                                       |
-| `failedJobs`    | Cancels all runs of own workflow that have matching jobs that failed.  | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
-| `namedJobs`     | Cancels all runs of own workflow that have matching jobs.              | Cancels all runs of the `sourceRunId` workflow that have matching jobs.             |
+| Cancel Mode              | No `sourceRunId` specified                                                   | The `sourceRunId` set to `${{ github.event.workflow_run.id }}`                      |
+|--------------------------|------------------------------------------------------------------------------|-------------------------------------------------------------------------------------|
+| `duplicates`             | Cancels duplicate runs from the same repo/branch as current run.             | Cancels duplicate runs for the same repo/branch as the source run.                  |
+| `allDuplicates`          | Cancels duplicate runs from all running workflows.                           | Cancels duplicate runs from all running workflows.                                  |
+| `self`                   | Cancels self run.                                                            | Cancel the `sourceRunId` run.                                                       |
+| `failedJobs`             | Cancels all runs of own workflow that have matching jobs that failed.        | Cancels all runs of the `sourceRunId` workflow that have matching jobs that failed. |
+| `namedJobs`              | Cancels all runs of own workflow that have matching jobs.                    | Cancels all runs of the `sourceRunId` workflow that have matching jobs.             |
+| `allDuplicatedNamedJobs` | Cancels all duplicate runs of own workflow that share matching jobs pattern. | Cancels all runs of the `sourceRunId` workflow that share matching job pattern.     |
 
 
 ## Outputs
@@ -163,7 +165,7 @@ jobs:
     name: "Cancel duplicate workflow runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: allDuplicates
@@ -231,7 +233,7 @@ jobs:
     name: "Cancel duplicate workflow runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
@@ -292,7 +294,7 @@ jobs:
       sourceHeadSha: ${{ steps.cancel.outputs.sourceHeadSha }}
       sourceEvent: ${{ steps.cancel.outputs.sourceEvent }}
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         id: cancel
         name: "Cancel duplicate CI runs"
         with:
@@ -305,7 +307,7 @@ jobs:
             that you will not see in the list of checks.
 
             You can checks the status of those images in:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel duplicate Cancelling runs"
         with:
           cancelMode: namedJobs
@@ -355,7 +357,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self CI workflow run"
-        uses: potiuk/cancel-workflow-runs@v4_1
+        uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: self
           notifyPRCancel: true
@@ -384,7 +386,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self Cancelling workflow run"
-        uses: potiuk/cancel-workflow-runs@v4_1
+        uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: self
           notifyPRCancel: true
@@ -417,7 +419,7 @@ jobs:
     name: "Fail fast CI runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Fail fast CI runs"
         with:
           cancelMode: failedJobs
@@ -458,7 +460,7 @@ jobs:
     name: "Fail fast CI runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Fail fast CI. Source run: ${{ github.event.workflow_run.id }}"
         id: cancel-failed
         with:
@@ -481,7 +483,7 @@ jobs:
             echo "::set-output name=matching-regexp::${REGEXP}"
       - name: "Cancel triggered 'Cancelling' runs for the cancelled failed runs"
         if: steps.cancel-failed.outputs.cancelledRuns != '[]'
-        uses: potiuk/cancel-workflow-runs@v4_1
+        uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: namedJobs
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -516,7 +518,7 @@ jobs:
     name: "Fail fast Canceling runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Fail fast Canceling runs"
         with:
           cancelMode: failedJobs
@@ -541,7 +543,7 @@ on:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self CI workflow run"
-        uses: potiuk/cancel-workflow-runs@v4_1
+        uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: duplicates
           cancelFutureDuplicates: true
@@ -549,6 +551,42 @@ on:
           workflowFileName: other_workflow.yml
 ```
 
+### Cancel all duplicates for named jobs
+
+Cancels all duplicated runs for all jobs that match specified regular expression.
+Note that it does not take into account the branch of the runs. It will cancel all duplicates with
+the same match for jobs, no matter what branch originated it.
+
+This is useful in case of job names generated dynamically.
+
+In the case below, for all the runs that have job names generated containing Branch/Repo/Event combination
+that have the same match, the duplicates will get cancelled leaving only the most recent run for each exact
+match.
+
+Note that the match must be identical. If there are two jobs that have a different Branch
+they will both match the same pattern, but they are not considered duplicates.
+
+```yaml
+on:
+  push:
+  workflow_run:
+    workflows: ['CI']
+    types: ['requested']
+
+jobs:
+  cancel-self-failed-runs:
+    name: "Cancel the self workflow run"
+    runs-on: ubuntu-latest
+    steps:
+      - uses: potiuk/cancel-workflow-runs@master
+        name: "Cancel past CI runs"
+        with:
+          cancelMode: allDuplicatedNamedJobs
+          token: ${{ secrets.GITHUB_TOKEN }}
+          jobNameRegexps: '["Branch: .* Repo: .* Event: .* "]'
+          notifyPRCancel: true
+```
+
 
 
 ## Repositories that do not use Pull Requests from forks
@@ -582,7 +620,7 @@ jobs:
     name: "Cancel duplicate workflow runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel duplicate workflow runs"
         with:
           cancelMode: duplicates
@@ -606,7 +644,7 @@ jobs:
     runs-on: ubuntu-latest
     steps:
       - name: "Cancel the self workflow run"
-        uses: potiuk/cancel-workflow-runs@v4_1
+        uses: potiuk/cancel-workflow-runs@master
         with:
           cancelMode: self
           token: ${{ secrets.GITHUB_TOKEN }}
@@ -632,7 +670,7 @@ jobs:
     name: "Cancel failed runs"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel failed runs"
         with:
           cancelMode: failedJobs
@@ -664,7 +702,7 @@ jobs:
     name: "Cancel the self workflow run"
     runs-on: ubuntu-latest
     steps:
-      - uses: potiuk/cancel-workflow-runs@v4_1
+      - uses: potiuk/cancel-workflow-runs@master
         name: "Cancel past CI runs"
         with:
           cancelMode: namedJobs
diff --git a/dist/index.js b/dist/index.js
index 6f047aa..2a806db 100644
--- a/dist/index.js
+++ b/dist/index.js
@@ -1485,15 +1485,17 @@ var CancelMode;
     CancelMode["SELF"] = "self";
     CancelMode["FAILED_JOBS"] = "failedJobs";
     CancelMode["NAMED_JOBS"] = "namedJobs";
+    CancelMode["ALL_DUPLICATED_NAMED_JOBS"] = "allDuplicatedNamedJobs";
 })(CancelMode || (CancelMode = {}));
 /**
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
- * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @param triggeringRunInfo the object identifying the triggering workflow
  * @returns the unique string id for the source group
  */
-function getSourceGroupId(sourceGroup) {
-    return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`;
+function getSourceGroupId(triggeringRunInfo) {
+    return (`:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+        `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`);
 }
 /**
  * Creates query parameters selecting all runs that share the same source group as we have. This can
@@ -1501,18 +1503,18 @@ function getSourceGroupId(sourceGroup) {
  *
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
- * @param mySourceGroup - source group of the originating run
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
-function createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup) {
+function createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo) {
     const request = {
         owner: repositoryInfo.owner,
         repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: mySourceGroup.workflowId,
+        workflow_id: triggeringRunInfo.workflowId,
         status,
-        branch: mySourceGroup.headBranch,
-        event: mySourceGroup.eventName
+        branch: triggeringRunInfo.headBranch,
+        event: triggeringRunInfo.eventName
     };
     return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
 }
@@ -1520,19 +1522,18 @@ function createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup
  * Creates query parameters selecting only specific run Id.
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
- * @param workflowId - Id of the workflow to retrieve
- * @param runId - run Id to retrieve
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
-function createListRunsQuerySpecificRunId(repositoryInfo, status, workflowId, runId) {
+function createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo) {
     const request = {
         owner: repositoryInfo.owner,
         repo: repositoryInfo.repo,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        workflow_id: workflowId,
+        workflow_id: triggeringRunInfo.workflowId,
         status,
         // eslint-disable-next-line @typescript-eslint/camelcase
-        run_id: runId.toString()
+        run_id: triggeringRunInfo.runId.toString()
     };
     return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request);
 }
@@ -1572,15 +1573,33 @@ function createJobsForWorkflowRunQuery(repositoryInfo, runId) {
  * Returns true if the string matches any of the regexps in array of regexps
  * @param stringToMatch string to match
  * @param regexps array of regexp to match the string against
- * @return true if there is a match
+ * @return array of [matched (boolean), [array of matched strings]]
  */
 function matchInArray(stringToMatch, regexps) {
+    let matched = false;
+    const allMatches = [];
     for (const regexp of regexps) {
-        if (stringToMatch.match(regexp)) {
-            return true;
+        const match = stringToMatch.match(regexp);
+        if (match) {
+            matched = true;
+            allMatches.push(match[0]);
         }
     }
-    return false;
+    return [matched, allMatches];
+}
+/**
+ * Adds workflow run to the array in the map indexed by key.
+ * @param key key to use
+ * @param runItem run Item to add
+ * @param mapOfWorkflowRunCandidates map of workflow runs to add the run item to
+ */
+function addWorkflowRunToMap(key, runItem, mapOfWorkflowRunCandidates) {
+    let arrayOfRuns = mapOfWorkflowRunCandidates.get(key);
+    if (arrayOfRuns === undefined) {
+        arrayOfRuns = [];
+        mapOfWorkflowRunCandidates.set(key, arrayOfRuns);
+    }
+    arrayOfRuns.push(runItem);
 }
 /**
  * Returns true if the runId specified has jobs matching the regexp and optionally checks if those
@@ -1591,7 +1610,7 @@ function matchInArray(stringToMatch, regexps) {
  * @param runId - Id of the run to retrieve jobs for
  * @param jobNameRegexps - array of job name regexps
  * @param checkIfFailed - whether to check the 'failed' status of matched jobs
- * @return true if there is a match
+ * @return array of [matched (boolean), array of matches]
  */
 function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed) {
     var e_1, _a;
@@ -1603,18 +1622,22 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
         else {
             core.info(`\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`);
         }
+        const allMatches = [];
+        let matched = false;
         try {
             for (var _b = __asyncValues(repositoryInfo.octokit.paginate.iterator(listJobs)), _c; _c = yield _b.next(), !_c.done;) {
                 const item = _c.value;
-                for (const job of item.data.jobs) {
+                for (const job of item.data) {
                     core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`);
-                    if (matchInArray(job.name, jobNameRegexps)) {
+                    const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps);
+                    if (jobMatched) {
+                        allMatches.push(...jobMatches);
+                        matched = true;
                         if (checkIfFailed) {
                             // Only fail the build if one of the matching jobs fail
                             if (job.conclusion === 'failure') {
                                 core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
-                                    ` Cancelling run.`);
-                                return true;
+                                    ` It will be added to the candidates.`);
                             }
                             else {
                                 core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -1623,8 +1646,8 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
                         }
                         else {
                             // Fail the build if any of the job names match
-                            core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. Cancelling run.`);
-                            return true;
+                            core.info(`    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
+                                `It will be added to the candidates.`);
                         }
                     }
                 }
@@ -1637,7 +1660,7 @@ function jobsMatchingNames(repositoryInfo, runId, jobNameRegexps, checkIfFailed)
             }
             finally { if (e_1) throw e_1.error; }
         }
-        return false;
+        return [matched, allMatches];
     });
 }
 /**
@@ -1711,67 +1734,74 @@ function getWorkflowRuns(repositoryInfo, statusValues, cancelMode, createListRun
 /**
  * True if the request is candidate for cancelling in case of duplicate deletion
  * @param runItem item to check
- * @param headRepo head Repo that we are checking against
  * @param cancelFutureDuplicates whether future duplicates are being cancelled
- * @param sourceRunId the source Run Id that originates the request
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if we determine that the run Id should be cancelled
  */
-function isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId) {
+function checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates) {
     const runHeadRepo = runItem.head_repository.full_name;
-    if (headRepo !== undefined && runHeadRepo !== headRepo) {
+    if (triggeringRunInfo.headRepo !== undefined &&
+        runHeadRepo !== triggeringRunInfo.headRepo) {
         core.info(`\nThe run ${runItem.id} is from a different ` +
-            `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`);
-        return false;
+            `repo: ${runHeadRepo} (expected ${triggeringRunInfo.headRepo}). Not adding as candidate to cancel.\n`);
     }
     if (cancelFutureDuplicates) {
         core.info(`\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`);
-        return true;
+        addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
     }
     else {
-        if (runItem.id === sourceRunId) {
+        if (runItem.id === triggeringRunInfo.runId) {
             core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`);
-            return false;
         }
-        else if (runItem.id > sourceRunId) {
-            core.info(`\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`);
-            return false;
+        else if (runItem.id > triggeringRunInfo.runId) {
+            core.info(`\nThe run ${runItem.id} is started later than my own ` +
+                `run ${triggeringRunInfo.runId}. Not returning it\n`);
         }
         core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`);
-        return true;
     }
 }
 /**
- * Should the run is candidate for cancelling in SELF cancelling mode?
+ * Should the run be candidate for cancelling in SELF cancelling mode?
  * @param runItem run item
- * @param sourceRunId source run id
- * @return true if the run item is self
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  */
-function isCandidateForCancellingSelf(runItem, sourceRunId) {
-    if (runItem.id === sourceRunId) {
-        core.info(`\nReturning the "source" run: ${runItem.id}.\n`);
-        return true;
-    }
-    else {
-        return false;
+function checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
+    if (runItem.id === triggeringRunInfo.runId) {
+        core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`);
+        addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
     }
 }
 /**
+ * Should the run be candidate for cancelling of all duplicates
+ * @param runItem run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
+ */
+function checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates) {
+    core.info(`\nAdding the run: ${runItem.id} to candidates.\n`);
+    addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
+}
+/**
  * Should the run is candidate for cancelling in naming job cancelling mode?
  * @param repositoryInfo - information about the repository used
  * @param runItem run item
  * @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if the run item contains jobs with names matching the pattern
  */
-function isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+function checkCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates) {
     return __awaiter(this, void 0, void 0, function* () {
         // Cancel all jobs that have failed jobs (no matter when started)
-        if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)) {
-            core.info(`\nSome jobs have matching names in ${runItem.id} . Returning it.\n`);
-            return true;
+        const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false);
+        if (matched) {
+            core.info(`\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`);
+            addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
         }
         else {
-            core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`);
-            return false;
+            core.info(`\nNone of the jobs match name in ${runItem.id}.\n`);
         }
     });
 }
@@ -1780,67 +1810,92 @@ function isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRege
  * @param repositoryInfo - information about the repository used
  * @param runItem run item
  * @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if the run item contains failed jobs with names matching the pattern
  */
-function isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps) {
+function checkCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates) {
     return __awaiter(this, void 0, void 0, function* () {
         // Cancel all jobs that have failed jobs (no matter when started)
-        if (yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)) {
-            core.info(`\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`);
-            return true;
+        const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true);
+        if (matched) {
+            core.info(`\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`);
+            addWorkflowRunToMap(getSourceGroupId(triggeringRunInfo), runItem, mapOfWorkflowRunCandidates);
         }
         else {
-            core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`);
-            return false;
+            core.info(`\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`);
+        }
+    });
+}
+/**
+ * Checks if the run is candidate for duplicate cancelling of named jobs. It adds it to the map
+ * including the match as a key for duplicate detection.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param jobNamesRegexps - array of regexps to match job names against
+ * @param mapOfWorkflowRunCandidates - map of runs to update
+ */
+function checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRegexps, mapOfWorkflowRunCandidates) {
+    return __awaiter(this, void 0, void 0, function* () {
+        const [matched, allMatches] = yield jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false);
+        if (matched) {
+            for (const match of allMatches) {
+                // This is the only case where we are not using source group to cancelling candidates but
+                // the match of job names
+                addWorkflowRunToMap(match, runItem, mapOfWorkflowRunCandidates);
+            }
         }
     });
 }
 /**
- * Determines whether the run is candidate to be cancelled depending on the mode used
+ * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
+ * of workflow names if it is.
  * @param repositoryInfo - information about the repository used
  * @param runItem - run item
- * @param headRepo - head repository
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode
  * @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - what is the source run id
  * @param jobNamesRegexps - what are the regexps for job names
  * @param skipEventTypes - which events should be skipped
- * @return true if the run id is candidate for cancelling
+ * @param mapOfWorkflowRunCandidates - map of workflow runs to add candidates to
  */
-function isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNamesRegexps, skipEventTypes) {
+function checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNamesRegexps, skipEventTypes, mapOfWorkflowRunCandidates) {
     return __awaiter(this, void 0, void 0, function* () {
         if ('completed' === runItem.status.toString()) {
-            core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`);
-            return false;
+            core.info(`\nThe run ${runItem.id} is completed. Not adding as candidate to cancel.\n`);
+            return;
         }
         if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
             core.info(`\nThe run ${runItem.id} is (${runItem.event} event - not ` +
-                `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`);
-            return false;
+                `in ${CANCELLABLE_EVENT_TYPES}). Not adding as candidate to cancel.\n`);
+            return;
         }
         if (skipEventTypes.includes(runItem.event.toString())) {
             core.info(`\nThe run ${runItem.id} is (${runItem.event} event - ` +
-                `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`);
-            return false;
+                `it is in skipEventTypes ${skipEventTypes}). Not adding as candidate to cancel.\n`);
+            return;
         }
         if (cancelMode === CancelMode.FAILED_JOBS) {
-            return yield isCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps);
+            yield checkCandidateForCancellingFailedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.NAMED_JOBS) {
-            return yield isCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps);
+            yield checkCandidateForCancellingNamedJobs(repositoryInfo, runItem, jobNamesRegexps, triggeringRunInfo, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.SELF) {
-            return isCandidateForCancellingSelf(runItem, sourceRunId);
+            checkCandidateForCancellingSelf(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
-            return isCandidateForCancellingDuplicate(runItem, headRepo, cancelFutureDuplicates, sourceRunId);
+            checkCandidateForCancellingDuplicate(runItem, cancelFutureDuplicates, triggeringRunInfo, mapOfWorkflowRunCandidates);
         }
         else if (cancelMode === CancelMode.ALL_DUPLICATES) {
-            core.info(`Returning candidate ${runItem.id} for "all_duplicates" cancelling.`);
-            return true;
+            checkCandidateForAllDuplicates(runItem, triggeringRunInfo, mapOfWorkflowRunCandidates);
+        }
+        else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+            yield checkCandidateForDuplicateNamedJobs(repositoryInfo, runItem, jobNamesRegexps, mapOfWorkflowRunCandidates);
+            return;
         }
         else {
-            throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+            throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it!.\n`);
         }
     });
 }
@@ -1871,35 +1926,35 @@ function cancelRun(repositoryInfo, runId) {
  * @param repositoryInfo - information about the repository used
  * @param statusValues - status values we want to check
  * @param cancelMode - cancel mode to use
- * @param sourceWorkflowId - source workflow id
- * @param sourceRunId - source run id
- * @param sourceEventName - name of the source event
- * @param mySourceGroup - source of the run that originated it
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return map of the run items matching grouped by workflow run id
  */
-function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup) {
+function getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo) {
     return __awaiter(this, void 0, void 0, function* () {
         return yield getWorkflowRuns(repositoryInfo, statusValues, cancelMode, function (status) {
             if (cancelMode === CancelMode.SELF) {
                 core.info(`\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`);
-                return createListRunsQuerySpecificRunId(repositoryInfo, status, sourceWorkflowId, sourceRunId);
+                    `Workflow ID:${triggeringRunInfo.workflowId},` +
+                    `Source Run id: ${triggeringRunInfo.runId}\n`);
+                return createListRunsQuerySpecificRunId(repositoryInfo, status, triggeringRunInfo);
             }
             else if (cancelMode === CancelMode.FAILED_JOBS ||
                 cancelMode === CancelMode.NAMED_JOBS ||
-                cancelMode === CancelMode.ALL_DUPLICATES) {
+                cancelMode === CancelMode.ALL_DUPLICATES ||
+                cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
                 core.info(`\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Status: ${status} Workflow ID:${sourceWorkflowId}\n`);
-                return createListRunsQueryAllRuns(repositoryInfo, status, sourceWorkflowId);
+                    `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`);
+                return createListRunsQueryAllRuns(repositoryInfo, status, triggeringRunInfo.workflowId);
             }
             else if (cancelMode === CancelMode.DUPLICATES) {
                 core.info(`\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-                    `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
-                    `Event name: ${sourceEventName}\n`);
-                return createListRunsQueryRunsSameSource(repositoryInfo, status, mySourceGroup);
+                    `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+                    `Head Branch: ${triggeringRunInfo.headBranch},` +
+                    `Event name: ${triggeringRunInfo.eventName}\n`);
+                return createListRunsQueryRunsSameSource(repositoryInfo, status, triggeringRunInfo);
             }
             else {
-                throw Error(`\nWrong cancel mode ${cancelMode}! This should never happen.\n`);
+                throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it.\n`);
             }
         });
     });
@@ -1948,42 +2003,28 @@ function findPullRequestForRunItem(repositoryInfo, runItem) {
     });
 }
 /**
- * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for cancelling
  * (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
  * same group are put together in one array - in a map indexed by the source group id.
  *
  * @param repositoryInfo - information about the repository used
- * @param headRepo - head repository the event comes from
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode to use
  * @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - source run id for the run
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
  * @param workflowRuns - map of workflow runs found
- * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ * @parm maps with string key and array of run items as value. The key might be
+ *       * source group id (allDuplicates mode)
+ *       * matching job name (allDuplicatedMatchingJobNames mode)
  */
-function filterAndMapWorkflowRunsToGroups(repositoryInfo, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns) {
+function filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns) {
     return __awaiter(this, void 0, void 0, function* () {
         const mapOfWorkflowRunCandidates = new Map();
         for (const [key, runItem] of workflowRuns) {
-            core.info(`\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
+            core.info(`\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
                 ` Created at ${runItem.created_at}\n`);
-            if (yield isCandidateForCancelling(repositoryInfo, runItem, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes)) {
-                const candidateSourceGroup = {
-                    workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
-                    headBranch: runItem.head_branch,
-                    headRepo: runItem.head_repository.full_name,
-                    eventName: runItem.event
-                };
-                const sourceGroupId = getSourceGroupId(candidateSourceGroup);
-                let workflowRunArray = mapOfWorkflowRunCandidates.get(sourceGroupId);
-                if (workflowRunArray === undefined) {
-                    workflowRunArray = [];
-                    mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray);
-                }
-                core.info(`The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`);
-                workflowRunArray.push(runItem);
-            }
+            yield checkCandidateForCancelling(repositoryInfo, runItem, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, mapOfWorkflowRunCandidates);
         }
         return mapOfWorkflowRunCandidates;
     });
@@ -2011,7 +2052,7 @@ function addCommentToPullRequest(repositoryInfo, pullRequestNumber, comment) {
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - my own run id
  * @param pullRequestNumber - number of pull request
- * @param reason reason for canceling
+ * @param reason reason for cancelling
  */
 function notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason) {
     return __awaiter(this, void 0, void 0, function* () {
@@ -2027,9 +2068,9 @@ function notifyPR(repositoryInfo, selfRunId, pullRequestNumber, reason) {
  * @param notifyPRCancel - whether to notify the PR when cancelling
  * @param selfRunId - what is the self run id
  * @param sourceGroupId - what is the source group id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
  */
-function cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason) {
+function cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info(`\n###### Cancelling runs for ${sourceGroupId} starting from the most recent  ##########\n` +
             `\n     Number of runs to cancel: ${sortedRunItems.length}\n`);
@@ -2051,39 +2092,40 @@ function cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRC
     });
 }
 /**
- * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
- * descending according to create date in each of the source groups and cancels them in that order -
- * optionally skipping the first found run in each source group in case of duplicates.
+ * Cancels found runs per group. It takes all the found groups, sorts them
+ * descending according to create date in each of the groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicate cancelling.
  *
  * @param repositoryInfo - information about the repository used
- * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param mapOfWorkflowRunCandidatesCandidatesToCancel map of all workflow run candidates
  * @param cancelMode - cancel mode
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param notifyPRCancel - whether to notify PRs with comments
  * @param selfRunId - self run Id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
  */
-function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason) {
+function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         const cancelledRuns = [];
-        for (const [sourceGroupId, candidatesArray] of mapOfWorkflowRunsCandidatesToCancel) {
+        for (const [groupId, candidatesArray] of mapOfWorkflowRunCandidatesCandidatesToCancel) {
             // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
             const sortedRunItems = candidatesArray.sort((runItem1, runItem2) => runItem2.created_at.localeCompare(runItem1.created_at));
             if (sortedRunItems.length > 0) {
                 if ((cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
-                    cancelMode === CancelMode.ALL_DUPLICATES) {
+                    cancelMode === CancelMode.ALL_DUPLICATES ||
+                    cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
                     core.info(`\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
-                        `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`);
+                        `duplicates for '${groupId}'. This one we are going to leave in peace!\n`);
                     sortedRunItems.shift();
                 }
                 if (sortedRunItems.length === 0) {
-                    core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`);
+                    core.info(`\nNo duplicates to cancel for ${groupId}!\n`);
                     continue;
                 }
-                cancelledRuns.push(...(yield cancelAllRunsInTheSourceGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, sourceGroupId, reason)));
+                cancelledRuns.push(...(yield cancelAllRunsInTheGroup(repositoryInfo, sortedRunItems, notifyPRCancel, selfRunId, groupId, reason)));
             }
             else {
-                core.info(`\n######  There are no runs to cancel for ${sourceGroupId} ##########\n`);
+                core.info(`\n######  There are no runs to cancel for ${groupId} ##########\n`);
             }
         }
         return cancelledRuns;
@@ -2093,14 +2135,7 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCanc
  * Find and cancels runs based on the criteria chosen.
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- *        (different than self event name for workflow_run)
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -2110,18 +2145,12 @@ function cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCanc
  * @param reason - reason for cancelling
  * @return array of canceled workflow run ids
  */
-function findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
+function findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason) {
     return __awaiter(this, void 0, void 0, function* () {
         const statusValues = ['queued', 'in_progress'];
-        const mySourceGroup = {
-            headBranch,
-            headRepo,
-            eventName: sourceEventName,
-            workflowId: sourceWorkflowId
-        };
-        const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, sourceWorkflowId, sourceRunId, sourceEventName, mySourceGroup);
-        const mapOfWorkflowRunsCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, headRepo, cancelMode, cancelFutureDuplicates, sourceRunId, jobNameRegexps, skipEventTypes, workflowRuns);
-        return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunsCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
+        const workflowRuns = yield getWorkflowRunsMatchingCriteria(repositoryInfo, statusValues, cancelMode, triggeringRunInfo);
+        const mapOfWorkflowRunCandidatesCandidatesToCancel = yield filterAndMapWorkflowRunsToGroups(repositoryInfo, triggeringRunInfo, cancelMode, cancelFutureDuplicates, jobNameRegexps, skipEventTypes, workflowRuns);
+        return yield cancelTheRunsPerGroup(repositoryInfo, mapOfWorkflowRunCandidatesCandidatesToCancel, cancelMode, cancelFutureDuplicates, notifyPRCancel, selfRunId, reason);
     });
 }
 /**
@@ -2138,12 +2167,12 @@ function getRequiredEnv(key) {
     return value;
 }
 /**
- * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * Gets source run using  of the runId - if this is a workflow run, it returns the information about the source run
  * @param repositoryInfo - information about the repository used
  * @param runId - run id of the run to check
- * @return information about the triggering workflow
+ * @return information about the triggering run
  */
-function getOrigin(repositoryInfo, runId) {
+function getTriggeringRunInfo(repositoryInfo, runId) {
     return __awaiter(this, void 0, void 0, function* () {
         const reply = yield repositoryInfo.octokit.actions.getWorkflowRun({
             owner: repositoryInfo.owner,
@@ -2160,6 +2189,8 @@ function getOrigin(repositoryInfo, runId) {
             pullRequest = yield findPullRequest(repositoryInfo, sourceRun.head_repository.owner.login, sourceRun.head_branch, sourceRun.head_sha);
         }
         return {
+            workflowId: retrieveWorkflowIdFromUrl(reply.data.workflow_url),
+            runId,
             headRepo: reply.data.head_repository.full_name,
             headBranch: reply.data.head_branch,
             headSha: reply.data.head_sha,
@@ -2174,14 +2205,7 @@ function getOrigin(repositoryInfo, runId) {
  *
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- *        (different than self event name for workflow_run)
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -2191,41 +2215,51 @@ function getOrigin(repositoryInfo, runId) {
  * @param jobNameRegexps - array of regular expressions to match hob names in case of named modes
  * @param skipEventTypes - array of event names that should be skipped
  */
-function performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
+function performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates) {
     return __awaiter(this, void 0, void 0, function* () {
         core.info('\n###################################################################################\n');
-        core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
-            `head repo ${headRepo}, headBranch: ${headBranch}, ` +
-            `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
+        core.info(`All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
+            `run id: ${triggeringRunInfo.runId}, ` +
+            `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
+            `sourceEventName: ${triggeringRunInfo.eventName}, ` +
+            `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`);
         core.info('\n###################################################################################\n');
         let reason = '';
         if (cancelMode === CancelMode.SELF) {
-            core.info(`# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`);
+            core.info(`# Cancelling source run: ${triggeringRunInfo.runId} ` +
+                `for workflow ${triggeringRunInfo.workflowId}.`);
             reason = notifyPRCancelMessage
                 ? notifyPRCancelMessage
                 : `The job has been cancelled by another workflow.`;
         }
         else if (cancelMode === CancelMode.FAILED_JOBS) {
-            core.info(`# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`);
+            core.info(`# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+                `where job names matching ${jobNameRegexps} failed.`);
             reason = `It has some failed jobs matching ${jobNameRegexps}.`;
         }
         else if (cancelMode === CancelMode.NAMED_JOBS) {
-            core.info(`# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`);
+            core.info(`# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+                `have job names matching ${jobNameRegexps}.`);
             reason = `It has jobs matching ${jobNameRegexps}.`;
         }
         else if (cancelMode === CancelMode.DUPLICATES) {
-            core.info(`# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`);
-            reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
+            core.info(`# Cancel duplicate runs for workflow ${triggeringRunInfo.workflowId} ` +
+                `for same triggering branch as own run Id.`);
+            reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
         }
         else if (cancelMode === CancelMode.ALL_DUPLICATES) {
-            core.info(`# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`);
-            reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`;
+            core.info(`# Cancel all duplicates runs started for workflow ${triggeringRunInfo.workflowId}.`);
+            reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
+        }
+        else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+            core.info(`# Cancel all duplicate named jobs matching the patterns ${jobNameRegexps}.`);
+            reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`;
         }
         else {
-            throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`);
+            throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`);
         }
         core.info('\n###################################################################################\n');
-        return yield findAndCancelRuns(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, headRepo, headBranch, sourceEventName, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
+        return yield findAndCancelRuns(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, cancelFutureDuplicates, notifyPRCancel, notifyPRMessageStart, jobNameRegexps, skipEventTypes, reason);
     });
 }
 /**
@@ -2235,27 +2269,27 @@ function performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRun
  *
  * @param repositoryInfo - information about the repository used
  * @param workflowFileName - optional workflow file name
- * @param sourceRunId - source run id of the workfow
+ * @param runId - run id of the workflow
  * @param selfRunId - self run id
  * @return workflow id that is associate with the workflow we are going to act on.
  */
-function retrieveWorkflowId(repositoryInfo, workflowFileName, sourceRunId, selfRunId) {
+function retrieveWorkflowId(repositoryInfo, workflowFileName, runId, selfRunId) {
     return __awaiter(this, void 0, void 0, function* () {
-        let sourceWorkflowId;
+        let workflowId;
         if (workflowFileName) {
-            sourceWorkflowId = workflowFileName;
-            core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`);
+            workflowId = workflowFileName;
+            core.info(`\nFinding runs for another workflow found by ${workflowFileName} name: ${workflowId}\n`);
         }
         else {
-            sourceWorkflowId = yield getWorkflowId(repositoryInfo, sourceRunId);
-            if (sourceRunId === selfRunId) {
-                core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`);
+            workflowId = yield getWorkflowId(repositoryInfo, runId);
+            if (runId === selfRunId) {
+                core.info(`\nFinding runs for my own workflow ${workflowId}\n`);
             }
             else {
-                core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`);
+                core.info(`\nFinding runs for source workflow ${workflowId}\n`);
             }
         }
-        return sourceWorkflowId;
+        return workflowId;
     });
 }
 /**
@@ -2273,15 +2307,15 @@ function verboseOutput(name, value) {
  * are verified and in case od unexpected combination found, approrpriate error is raised.
  *
  * @param eventName - name of the event to act on
- * @param sourceRunId - run id of the triggering event
+ * @param runId - run id of the triggering event
  * @param selfRunId - our own run id
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
  * @param jobNameRegexps - array of regular expression of job names
  */
-function performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps) {
+function performSanityChecks(eventName, runId, selfRunId, cancelMode, cancelFutureDuplicates, jobNameRegexps) {
     if (eventName === 'workflow_run' &&
-        sourceRunId === selfRunId &&
+        runId === selfRunId &&
         cancelMode === CancelMode.DUPLICATES) {
         throw Error(`You cannot run "workflow_run" in ${cancelMode} cancelMode without "sourceId" input.` +
             'It will likely not work as you intended - it will cancel runs which are not duplicates!' +
@@ -2293,9 +2327,19 @@ function performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, canc
             CancelMode.SELF,
             CancelMode.ALL_DUPLICATES
         ].includes(cancelMode)) {
-        throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`);
+        throw Error(`You cannot specify jobNamesRegexps on ${cancelMode} cancelMode.`);
     }
-    if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+    if (jobNameRegexps.length === 0 &&
+        [
+            CancelMode.NAMED_JOBS,
+            CancelMode.FAILED_JOBS,
+            CancelMode.ALL_DUPLICATED_NAMED_JOBS
+        ].includes(cancelMode)) {
+        throw Error(`You must specify jobNamesRegexps on ${cancelMode} cancelMode.`);
+    }
+    if ((cancelMode === CancelMode.ALL_DUPLICATES ||
+        cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) &&
+        !cancelFutureDuplicates) {
         throw Error(`The  ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`);
     }
 }
@@ -2303,42 +2347,39 @@ function performSanityChecks(eventName, sourceRunId, selfRunId, cancelMode, canc
  * Produces basic outputs for the action. This does not include cancelled workflow run id - those are
  * set after cancelling is done.
  *
- * @param triggeringWorkflowInfo
+ * @param triggeringRunInfo
  */
-function produceBasicOutputs(triggeringWorkflowInfo) {
-    verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo);
-    verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch);
-    verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha);
-    verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName);
-    verboseOutput('pullRequestNumber', triggeringWorkflowInfo.pullRequest
-        ? triggeringWorkflowInfo.pullRequest.number.toString()
-        : '');
-    verboseOutput('mergeCommitSha', triggeringWorkflowInfo.mergeCommitSha
-        ? triggeringWorkflowInfo.mergeCommitSha
+function produceBasicOutputs(triggeringRunInfo) {
+    verboseOutput('sourceHeadRepo', triggeringRunInfo.headRepo);
+    verboseOutput('sourceHeadBranch', triggeringRunInfo.headBranch);
+    verboseOutput('sourceHeadSha', triggeringRunInfo.headSha);
+    verboseOutput('sourceEvent', triggeringRunInfo.eventName);
+    verboseOutput('pullRequestNumber', triggeringRunInfo.pullRequest
+        ? triggeringRunInfo.pullRequest.number.toString()
         : '');
-    verboseOutput('targetCommitSha', triggeringWorkflowInfo.mergeCommitSha
-        ? triggeringWorkflowInfo.mergeCommitSha
-        : triggeringWorkflowInfo.headSha);
+    verboseOutput('mergeCommitSha', triggeringRunInfo.mergeCommitSha ? triggeringRunInfo.mergeCommitSha : '');
+    verboseOutput('targetCommitSha', triggeringRunInfo.mergeCommitSha
+        ? triggeringRunInfo.mergeCommitSha
+        : triggeringRunInfo.headSha);
 }
 /**
  * Notifies the PR that the action has started.
  *
  * @param repositoryInfo information about the repository
- * @param triggeringWorkflowInfo information about the triggering workflow
- * @param sourceRunId run id of the source workflow
+ * @param triggeringRunInfo information about the triggering workflow
  * @param selfRunId self run id
  * @param notifyPRMessageStart whether to notify about the start of the action
  */
-function notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart) {
+function notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart) {
     return __awaiter(this, void 0, void 0, function* () {
-        if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+        if (notifyPRMessageStart && triggeringRunInfo.pullRequest) {
             const selfWorkflowRunUrl = `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
                 `/actions/runs/${selfRunId}`;
             yield repositoryInfo.octokit.issues.createComment({
                 owner: repositoryInfo.owner,
                 repo: repositoryInfo.repo,
                 // eslint-disable-next-line @typescript-eslint/camelcase
-                issue_number: triggeringWorkflowInfo.pullRequest.number,
+                issue_number: triggeringRunInfo.pullRequest.number,
                 body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
             });
         }
@@ -2383,10 +2424,10 @@ function run() {
             `Event name: ${eventName}, CancelMode: ${cancelMode}, ` +
             `sourceWorkflowId: ${sourceWorkflowId}, sourceRunId: ${sourceRunId}, selfRunId: ${selfRunId}, ` +
             `jobNames: ${jobNameRegexps}`);
-        const triggeringWorkflowInfo = yield getOrigin(repositoryInfo, sourceRunId);
-        produceBasicOutputs(triggeringWorkflowInfo);
-        yield notifyActionStart(repositoryInfo, triggeringWorkflowInfo, sourceRunId, selfRunId, notifyPRMessageStart);
-        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, sourceWorkflowId, sourceRunId, triggeringWorkflowInfo.headRepo, triggeringWorkflowInfo.headBranch, triggeringWorkflowInfo.eventName, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
+        const triggeringRunInfo = yield getTriggeringRunInfo(repositoryInfo, sourceRunId);
+        produceBasicOutputs(triggeringRunInfo);
+        yield notifyActionStart(repositoryInfo, triggeringRunInfo, selfRunId, notifyPRMessageStart);
+        const cancelledRuns = yield performCancelJob(repositoryInfo, selfRunId, triggeringRunInfo, cancelMode, notifyPRCancel, notifyPRCancelMessage, notifyPRMessageStart, jobNameRegexps, skipEventTypes, cancelFutureDuplicates);
         verboseOutput('cancelledRuns', JSON.stringify(cancelledRuns));
     });
 }
diff --git a/src/main.ts b/src/main.ts
index 19b00b3..abf39f1 100644
--- a/src/main.ts
+++ b/src/main.ts
@@ -21,21 +21,8 @@ enum CancelMode {
   ALL_DUPLICATES = 'allDuplicates',
   SELF = 'self',
   FAILED_JOBS = 'failedJobs',
-  NAMED_JOBS = 'namedJobs'
-}
-
-/**
- * Stores information uniquely identifying the run by it's triggering source:
- * It is the workflow id, Head Repo and Branch the change originates from and event name that originates it.
- *
- * All Runs coming from the same Pull Request share the same Source Group. Also all pushes to master
- * share the same Source Group
- */
-interface WorkflowRunSourceGroup {
-  workflowId: number | string
-  headRepo: string
-  headBranch: string
-  eventName: string
+  NAMED_JOBS = 'namedJobs',
+  ALL_DUPLICATED_NAMED_JOBS = 'allDuplicatedNamedJobs'
 }
 
 /**
@@ -51,7 +38,9 @@ interface RepositoryInfo {
 /**
  * Stores information about the workflow info that triggered the current workflow.
  */
-interface TriggeringWorkflowInfo {
+interface TriggeringRunInfo {
+  workflowId: number
+  runId: number
   headRepo: string
   headBranch: string
   headSha: string
@@ -63,11 +52,14 @@ interface TriggeringWorkflowInfo {
 /**
  * Converts the source of a run object into a string that can be used as map key in maps where we keep
  * arrays of runs per source group
- * @param sourceGroup the object identifying the source of the run (Pull Request, Master Push)
+ * @param triggeringRunInfo the object identifying the triggering workflow
  * @returns the unique string id for the source group
  */
-function getSourceGroupId(sourceGroup: WorkflowRunSourceGroup): string {
-  return `:${sourceGroup.workflowId}:${sourceGroup.headRepo}:${sourceGroup.headBranch}:${sourceGroup.eventName}`
+function getSourceGroupId(triggeringRunInfo: TriggeringRunInfo): string {
+  return (
+    `:${triggeringRunInfo.workflowId}:${triggeringRunInfo.headRepo}` +
+    `:${triggeringRunInfo.headBranch}:${triggeringRunInfo.eventName}`
+  )
 }
 
 /**
@@ -76,22 +68,22 @@ function getSourceGroupId(sourceGroup: WorkflowRunSourceGroup): string {
  *
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
- * @param mySourceGroup - source group of the originating run
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
 function createListRunsQueryRunsSameSource(
   repositoryInfo: RepositoryInfo,
   status: string,
-  mySourceGroup: WorkflowRunSourceGroup
+  triggeringRunInfo: TriggeringRunInfo
 ): rest.RequestOptions {
   const request = {
     owner: repositoryInfo.owner,
     repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    workflow_id: mySourceGroup.workflowId,
+    workflow_id: triggeringRunInfo.workflowId,
     status,
-    branch: mySourceGroup.headBranch,
-    event: mySourceGroup.eventName
+    branch: triggeringRunInfo.headBranch,
+    event: triggeringRunInfo.eventName
   }
   return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
@@ -99,24 +91,22 @@ function createListRunsQueryRunsSameSource(
  * Creates query parameters selecting only specific run Id.
  * @param repositoryInfo - information about the repository used
  * @param status - status of the run that we are querying for
- * @param workflowId - Id of the workflow to retrieve
- * @param runId - run Id to retrieve
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return query parameters merged with the listWorkflowRuns criteria
  */
 function createListRunsQuerySpecificRunId(
   repositoryInfo: RepositoryInfo,
   status: string,
-  workflowId: number | string,
-  runId: number
+  triggeringRunInfo: TriggeringRunInfo
 ): rest.RequestOptions {
   const request = {
     owner: repositoryInfo.owner,
     repo: repositoryInfo.repo,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    workflow_id: workflowId,
+    workflow_id: triggeringRunInfo.workflowId,
     status,
     // eslint-disable-next-line @typescript-eslint/camelcase
-    run_id: runId.toString()
+    run_id: triggeringRunInfo.runId.toString()
   }
   return repositoryInfo.octokit.actions.listWorkflowRuns.endpoint.merge(request)
 }
@@ -168,15 +158,44 @@ function createJobsForWorkflowRunQuery(
  * Returns true if the string matches any of the regexps in array of regexps
  * @param stringToMatch string to match
  * @param regexps array of regexp to match the string against
- * @return true if there is a match
+ * @return array of [matched (boolean), [array of matched strings]]
  */
-function matchInArray(stringToMatch: string, regexps: string[]): boolean {
+function matchInArray(
+  stringToMatch: string,
+  regexps: string[]
+): [boolean, string[]] {
+  let matched = false
+  const allMatches: string[] = []
   for (const regexp of regexps) {
-    if (stringToMatch.match(regexp)) {
-      return true
+    const match = stringToMatch.match(regexp)
+    if (match) {
+      matched = true
+      allMatches.push(match[0])
     }
   }
-  return false
+  return [matched, allMatches]
+}
+
+/**
+ * Adds workflow run to the array in the map indexed by key.
+ * @param key key to use
+ * @param runItem run Item to add
+ * @param mapOfWorkflowRunCandidates map of workflow runs to add the run item to
+ */
+function addWorkflowRunToMap(
+  key: string,
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): void {
+  let arrayOfRuns = mapOfWorkflowRunCandidates.get(key)
+  if (arrayOfRuns === undefined) {
+    arrayOfRuns = []
+    mapOfWorkflowRunCandidates.set(key, arrayOfRuns)
+  }
+  arrayOfRuns.push(runItem)
 }
 
 /**
@@ -188,14 +207,14 @@ function matchInArray(stringToMatch: string, regexps: string[]): boolean {
  * @param runId - Id of the run to retrieve jobs for
  * @param jobNameRegexps - array of job name regexps
  * @param checkIfFailed - whether to check the 'failed' status of matched jobs
- * @return true if there is a match
+ * @return array of [matched (boolean), array of matches]
  */
 async function jobsMatchingNames(
   repositoryInfo: RepositoryInfo,
   runId: number,
   jobNameRegexps: string[],
   checkIfFailed: boolean
-): Promise<boolean> {
+): Promise<[boolean, string[]]> {
   const listJobs = createJobsForWorkflowRunQuery(repositoryInfo, runId)
   if (checkIfFailed) {
     core.info(
@@ -206,18 +225,22 @@ async function jobsMatchingNames(
       `\nChecking if runId ${runId} has job names matching any of the ${jobNameRegexps}\n`
     )
   }
+  const allMatches: string[] = []
+  let matched = false
   for await (const item of repositoryInfo.octokit.paginate.iterator(listJobs)) {
-    for (const job of item.data.jobs) {
+    for (const job of item.data) {
       core.info(`    The job name: ${job.name}, Conclusion: ${job.conclusion}`)
-      if (matchInArray(job.name, jobNameRegexps)) {
+      const [jobMatched, jobMatches] = matchInArray(job.name, jobNameRegexps)
+      if (jobMatched) {
+        allMatches.push(...jobMatches)
+        matched = true
         if (checkIfFailed) {
           // Only fail the build if one of the matching jobs fail
           if (job.conclusion === 'failure') {
             core.info(
               `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps and it failed.` +
-                ` Cancelling run.`
+                ` It will be added to the candidates.`
             )
-            return true
           } else {
             core.info(
               `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps but it did not fail. ` +
@@ -227,14 +250,14 @@ async function jobsMatchingNames(
         } else {
           // Fail the build if any of the job names match
           core.info(
-            `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. Cancelling run.`
+            `    The Job ${job.name} matches one of the ${jobNameRegexps} regexps. ` +
+              `It will be added to the candidates.`
           )
-          return true
         }
       }
     }
   }
-  return false
+  return [matched, allMatches]
 }
 
 /**
@@ -310,86 +333,135 @@ async function getWorkflowRuns(
 /**
  * True if the request is candidate for cancelling in case of duplicate deletion
  * @param runItem item to check
- * @param headRepo head Repo that we are checking against
  * @param cancelFutureDuplicates whether future duplicates are being cancelled
- * @param sourceRunId the source Run Id that originates the request
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if we determine that the run Id should be cancelled
  */
-function isCandidateForCancellingDuplicate(
+function checkCandidateForCancellingDuplicate(
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
-  headRepo: string,
   cancelFutureDuplicates: boolean,
-  sourceRunId: number
-): boolean {
+  triggeringRunInfo: TriggeringRunInfo,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): void {
   const runHeadRepo = runItem.head_repository.full_name
-  if (headRepo !== undefined && runHeadRepo !== headRepo) {
+  if (
+    triggeringRunInfo.headRepo !== undefined &&
+    runHeadRepo !== triggeringRunInfo.headRepo
+  ) {
     core.info(
       `\nThe run ${runItem.id} is from a different ` +
-        `repo: ${runHeadRepo} (expected ${headRepo}). Not cancelling it\n`
+        `repo: ${runHeadRepo} (expected ${triggeringRunInfo.headRepo}). Not adding as candidate to cancel.\n`
     )
-    return false
   }
   if (cancelFutureDuplicates) {
     core.info(
       `\nCancel Future Duplicates: Returning run id that might be duplicate or my own run: ${runItem.id}.\n`
     )
-    return true
+    addWorkflowRunToMap(
+      getSourceGroupId(triggeringRunInfo),
+      runItem,
+      mapOfWorkflowRunCandidates
+    )
   } else {
-    if (runItem.id === sourceRunId) {
+    if (runItem.id === triggeringRunInfo.runId) {
       core.info(`\nThis is my own run ${runItem.id}. Not returning myself!\n`)
-      return false
-    } else if (runItem.id > sourceRunId) {
+    } else if (runItem.id > triggeringRunInfo.runId) {
       core.info(
-        `\nThe run ${runItem.id} is started later than my own run ${sourceRunId}. Not returning it\n`
+        `\nThe run ${runItem.id} is started later than my own ` +
+          `run ${triggeringRunInfo.runId}. Not returning it\n`
       )
-      return false
     }
     core.info(`\nFound duplicate of my own run: ${runItem.id}.\n`)
-    return true
   }
 }
 
 /**
- * Should the run is candidate for cancelling in SELF cancelling mode?
+ * Should the run be candidate for cancelling in SELF cancelling mode?
  * @param runItem run item
- * @param sourceRunId source run id
- * @return true if the run item is self
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  */
-function isCandidateForCancellingSelf(
+function checkCandidateForCancellingSelf(
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
-  sourceRunId: number
-): boolean {
-  if (runItem.id === sourceRunId) {
-    core.info(`\nReturning the "source" run: ${runItem.id}.\n`)
-    return true
-  } else {
-    return false
+  triggeringRunInfo: TriggeringRunInfo,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): void {
+  if (runItem.id === triggeringRunInfo.runId) {
+    core.info(`\nAdding the "source" run: ${runItem.id} to candidates.\n`)
+    addWorkflowRunToMap(
+      getSourceGroupId(triggeringRunInfo),
+      runItem,
+      mapOfWorkflowRunCandidates
+    )
   }
 }
 
 /**
+ * Should the run be candidate for cancelling of all duplicates
+ * @param runItem run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
+ */
+function checkCandidateForAllDuplicates(
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  triggeringRunInfo: TriggeringRunInfo,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): void {
+  core.info(`\nAdding the run: ${runItem.id} to candidates.\n`)
+  addWorkflowRunToMap(
+    getSourceGroupId(triggeringRunInfo),
+    runItem,
+    mapOfWorkflowRunCandidates
+  )
+}
+
+/**
  * Should the run is candidate for cancelling in naming job cancelling mode?
  * @param repositoryInfo - information about the repository used
  * @param runItem run item
  * @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if the run item contains jobs with names matching the pattern
  */
-async function isCandidateForCancellingNamedJobs(
+async function checkCandidateForCancellingNamedJobs(
   repositoryInfo: RepositoryInfo,
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
-  jobNamesRegexps: string[]
-): Promise<boolean> {
+  jobNamesRegexps: string[],
+  triggeringRunInfo: TriggeringRunInfo,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): Promise<void> {
   // Cancel all jobs that have failed jobs (no matter when started)
-  if (
-    await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, false)
-  ) {
+  const [matched, allMatches] = await jobsMatchingNames(
+    repositoryInfo,
+    runItem.id,
+    jobNamesRegexps,
+    false
+  )
+  if (matched) {
     core.info(
-      `\nSome jobs have matching names in ${runItem.id} . Returning it.\n`
+      `\nSome jobs have matching names in ${runItem.id}: ${allMatches}. Adding it candidates.\n`
+    )
+    addWorkflowRunToMap(
+      getSourceGroupId(triggeringRunInfo),
+      runItem,
+      mapOfWorkflowRunCandidates
     )
-    return true
   } else {
-    core.info(`\nNone of the jobs match name in ${runItem.id}. Returning it.\n`)
-    return false
+    core.info(`\nNone of the jobs match name in ${runItem.id}.\n`)
   }
 }
 
@@ -398,99 +470,165 @@ async function isCandidateForCancellingNamedJobs(
  * @param repositoryInfo - information about the repository used
  * @param runItem run item
  * @param jobNamesRegexps array of regexps to match job names against
+ * @param triggeringRunInfo - information about the workflow that triggered the run
+ * @param mapOfWorkflowRunCandidates - map of the workflow runs to add candidates to
  * @return true if the run item contains failed jobs with names matching the pattern
  */
-async function isCandidateForCancellingFailedJobs(
+async function checkCandidateForCancellingFailedJobs(
   repositoryInfo: RepositoryInfo,
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
-  jobNamesRegexps: string[]
-): Promise<boolean> {
+  jobNamesRegexps: string[],
+  triggeringRunInfo: TriggeringRunInfo,
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): Promise<void> {
   // Cancel all jobs that have failed jobs (no matter when started)
-  if (
-    await jobsMatchingNames(repositoryInfo, runItem.id, jobNamesRegexps, true)
-  ) {
+  const [matched, allMatches] = await jobsMatchingNames(
+    repositoryInfo,
+    runItem.id,
+    jobNamesRegexps,
+    true
+  )
+  if (matched) {
     core.info(
-      `\nSome matching named jobs failed in ${runItem.id} . Cancelling it.\n`
+      `\nSome matching named jobs failed in ${runItem.id}: ${allMatches}. Adding it to candidates.\n`
+    )
+    addWorkflowRunToMap(
+      getSourceGroupId(triggeringRunInfo),
+      runItem,
+      mapOfWorkflowRunCandidates
     )
-    return true
   } else {
     core.info(
-      `\nNone of the matching jobs failed in ${runItem.id}. Not cancelling it.\n`
+      `\nNone of the matching jobs failed in ${runItem.id}. Not adding as candidate to cancel.\n`
     )
-    return false
   }
 }
 
 /**
- * Determines whether the run is candidate to be cancelled depending on the mode used
+ * Checks if the run is candidate for duplicate cancelling of named jobs. It adds it to the map
+ * including the match as a key for duplicate detection.
  * @param repositoryInfo - information about the repository used
  * @param runItem - run item
- * @param headRepo - head repository
+ * @param jobNamesRegexps - array of regexps to match job names against
+ * @param mapOfWorkflowRunCandidates - map of runs to update
+ */
+async function checkCandidateForDuplicateNamedJobs(
+  repositoryInfo: RepositoryInfo,
+  runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
+  jobNamesRegexps: string[],
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): Promise<void> {
+  const [matched, allMatches] = await jobsMatchingNames(
+    repositoryInfo,
+    runItem.id,
+    jobNamesRegexps,
+    false
+  )
+  if (matched) {
+    for (const match of allMatches) {
+      // This is the only case where we are not using source group to cancelling candidates but
+      // the match of job names
+      addWorkflowRunToMap(match, runItem, mapOfWorkflowRunCandidates)
+    }
+  }
+}
+
+/**
+ * Determines whether the run is candidate to be cancelled depending on the mode used and add it to the map
+ * of workflow names if it is.
+ * @param repositoryInfo - information about the repository used
+ * @param runItem - run item
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode
  * @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - what is the source run id
  * @param jobNamesRegexps - what are the regexps for job names
  * @param skipEventTypes - which events should be skipped
- * @return true if the run id is candidate for cancelling
+ * @param mapOfWorkflowRunCandidates - map of workflow runs to add candidates to
  */
-async function isCandidateForCancelling(
+async function checkCandidateForCancelling(
   repositoryInfo: RepositoryInfo,
   runItem: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem,
-  headRepo: string,
+  triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
-  sourceRunId: number,
   jobNamesRegexps: string[],
-  skipEventTypes: string[]
-): Promise<boolean> {
+  skipEventTypes: string[],
+  mapOfWorkflowRunCandidates: Map<
+    string,
+    rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
+  >
+): Promise<void> {
   if ('completed' === runItem.status.toString()) {
-    core.info(`\nThe run ${runItem.id} is completed. Not cancelling it.\n`)
-    return false
+    core.info(
+      `\nThe run ${runItem.id} is completed. Not adding as candidate to cancel.\n`
+    )
+    return
   }
   if (!CANCELLABLE_EVENT_TYPES.includes(runItem.event.toString())) {
     core.info(
       `\nThe run ${runItem.id} is (${runItem.event} event - not ` +
-        `in ${CANCELLABLE_EVENT_TYPES}). Not cancelling it.\n`
+        `in ${CANCELLABLE_EVENT_TYPES}). Not adding as candidate to cancel.\n`
     )
-    return false
+    return
   }
   if (skipEventTypes.includes(runItem.event.toString())) {
     core.info(
       `\nThe run ${runItem.id} is (${runItem.event} event - ` +
-        `it is in skipEventTypes ${skipEventTypes}). Not cancelling it.\n`
+        `it is in skipEventTypes ${skipEventTypes}). Not adding as candidate to cancel.\n`
     )
-    return false
+    return
   }
   if (cancelMode === CancelMode.FAILED_JOBS) {
-    return await isCandidateForCancellingFailedJobs(
+    await checkCandidateForCancellingFailedJobs(
       repositoryInfo,
       runItem,
-      jobNamesRegexps
+      jobNamesRegexps,
+      triggeringRunInfo,
+      mapOfWorkflowRunCandidates
     )
   } else if (cancelMode === CancelMode.NAMED_JOBS) {
-    return await isCandidateForCancellingNamedJobs(
+    await checkCandidateForCancellingNamedJobs(
       repositoryInfo,
       runItem,
-      jobNamesRegexps
+      jobNamesRegexps,
+      triggeringRunInfo,
+      mapOfWorkflowRunCandidates
     )
   } else if (cancelMode === CancelMode.SELF) {
-    return isCandidateForCancellingSelf(runItem, sourceRunId)
+    checkCandidateForCancellingSelf(
+      runItem,
+      triggeringRunInfo,
+      mapOfWorkflowRunCandidates
+    )
   } else if (cancelMode === CancelMode.DUPLICATES) {
-    return isCandidateForCancellingDuplicate(
+    checkCandidateForCancellingDuplicate(
       runItem,
-      headRepo,
       cancelFutureDuplicates,
-      sourceRunId
+      triggeringRunInfo,
+      mapOfWorkflowRunCandidates
     )
   } else if (cancelMode === CancelMode.ALL_DUPLICATES) {
-    core.info(
-      `Returning candidate ${runItem.id} for "all_duplicates" cancelling.`
+    checkCandidateForAllDuplicates(
+      runItem,
+      triggeringRunInfo,
+      mapOfWorkflowRunCandidates
     )
-    return true
-  } else {
-    throw Error(
-      `\nWrong cancel mode ${cancelMode}! This should never happen.\n`
+  } else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+    await checkCandidateForDuplicateNamedJobs(
+      repositoryInfo,
+      runItem,
+      jobNamesRegexps,
+      mapOfWorkflowRunCandidates
     )
+    return
+  } else {
+    throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it!.\n`)
   }
 }
 
@@ -524,25 +662,14 @@ async function cancelRun(
  * @param repositoryInfo - information about the repository used
  * @param statusValues - status values we want to check
  * @param cancelMode - cancel mode to use
- * @param sourceWorkflowId - source workflow id
- * @param sourceRunId - source run id
- * @param sourceEventName - name of the source event
- * @param mySourceGroup - source of the run that originated it
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @return map of the run items matching grouped by workflow run id
  */
 async function getWorkflowRunsMatchingCriteria(
   repositoryInfo: RepositoryInfo,
   statusValues: string[],
-  cancelMode:
-    | CancelMode
-    | CancelMode.DUPLICATES
-    | CancelMode.ALL_DUPLICATES
-    | CancelMode.FAILED_JOBS
-    | CancelMode.NAMED_JOBS,
-  sourceWorkflowId: number | string,
-  sourceRunId: number,
-  sourceEventName: string,
-  mySourceGroup: WorkflowRunSourceGroup
+  cancelMode: CancelMode,
+  triggeringRunInfo: TriggeringRunInfo
 ): Promise<Map<number, rest.ActionsListWorkflowRunsResponseWorkflowRunsItem>> {
   return await getWorkflowRuns(
     repositoryInfo,
@@ -552,43 +679,43 @@ async function getWorkflowRunsMatchingCriteria(
       if (cancelMode === CancelMode.SELF) {
         core.info(
           `\nFinding runs for my own run: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Workflow ID:${sourceWorkflowId}, Source Run id: ${sourceRunId}\n`
+            `Workflow ID:${triggeringRunInfo.workflowId},` +
+            `Source Run id: ${triggeringRunInfo.runId}\n`
         )
         return createListRunsQuerySpecificRunId(
           repositoryInfo,
           status,
-          sourceWorkflowId,
-          sourceRunId
+          triggeringRunInfo
         )
       } else if (
         cancelMode === CancelMode.FAILED_JOBS ||
         cancelMode === CancelMode.NAMED_JOBS ||
-        cancelMode === CancelMode.ALL_DUPLICATES
+        cancelMode === CancelMode.ALL_DUPLICATES ||
+        cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS
       ) {
         core.info(
           `\nFinding runs for all runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Status: ${status} Workflow ID:${sourceWorkflowId}\n`
+            `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}\n`
         )
         return createListRunsQueryAllRuns(
           repositoryInfo,
           status,
-          sourceWorkflowId
+          triggeringRunInfo.workflowId
         )
       } else if (cancelMode === CancelMode.DUPLICATES) {
         core.info(
           `\nFinding duplicate runs: Owner: ${repositoryInfo.owner}, Repo: ${repositoryInfo.repo}, ` +
-            `Status: ${status} Workflow ID:${sourceWorkflowId}, Head Branch: ${mySourceGroup.headBranch},` +
-            `Event name: ${sourceEventName}\n`
+            `Status: ${status} Workflow ID:${triggeringRunInfo.workflowId}, ` +
+            `Head Branch: ${triggeringRunInfo.headBranch},` +
+            `Event name: ${triggeringRunInfo.eventName}\n`
         )
         return createListRunsQueryRunsSameSource(
           repositoryInfo,
           status,
-          mySourceGroup
+          triggeringRunInfo
         )
       } else {
-        throw Error(
-          `\nWrong cancel mode ${cancelMode}! This should never happen.\n`
-        )
+        throw Error(`\nWrong cancel mode ${cancelMode}! Please correct it.\n`)
       }
     }
   )
@@ -653,26 +780,26 @@ async function findPullRequestForRunItem(
 }
 
 /**
- * Maps found workflow runs into groups - filters out the workflows that are not eligible for canceling
+ * Maps found workflow runs into groups - filters out the workflows that are not eligible for cancelling
  * (depends on cancel Mode) and assigns each workflow to groups - where workflow runs from the
  * same group are put together in one array - in a map indexed by the source group id.
  *
  * @param repositoryInfo - information about the repository used
- * @param headRepo - head repository the event comes from
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode to use
  * @param cancelFutureDuplicates - whether to cancel future duplicates
- * @param sourceRunId - source run id for the run
  * @param jobNameRegexps - regexps for job names
  * @param skipEventTypes - array of event names to skip
  * @param workflowRuns - map of workflow runs found
- * @parm map where key is the source group id and value is array of workflow run item candidates to cancel
+ * @parm maps with string key and array of run items as value. The key might be
+ *       * source group id (allDuplicates mode)
+ *       * matching job name (allDuplicatedMatchingJobNames mode)
  */
 async function filterAndMapWorkflowRunsToGroups(
   repositoryInfo: RepositoryInfo,
-  headRepo: string,
+  triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
-  sourceRunId: number,
   jobNameRegexps: string[],
   skipEventTypes: string[],
   workflowRuns: Map<
@@ -685,40 +812,19 @@ async function filterAndMapWorkflowRunsToGroups(
   const mapOfWorkflowRunCandidates = new Map()
   for (const [key, runItem] of workflowRuns) {
     core.info(
-      `\nChecking run number: ${key}, RunId: ${runItem.id}, Url: ${runItem.url}. Status ${runItem.status},` +
+      `\nChecking run number: ${key} RunId: ${runItem.id} Url: ${runItem.url} Status ${runItem.status}` +
         ` Created at ${runItem.created_at}\n`
     )
-    if (
-      await isCandidateForCancelling(
-        repositoryInfo,
-        runItem,
-        headRepo,
-        cancelMode,
-        cancelFutureDuplicates,
-        sourceRunId,
-        jobNameRegexps,
-        skipEventTypes
-      )
-    ) {
-      const candidateSourceGroup: WorkflowRunSourceGroup = {
-        workflowId: retrieveWorkflowIdFromUrl(runItem.workflow_url),
-        headBranch: runItem.head_branch,
-        headRepo: runItem.head_repository.full_name,
-        eventName: runItem.event
-      }
-      const sourceGroupId = getSourceGroupId(candidateSourceGroup)
-      let workflowRunArray:
-        | rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
-        | undefined = mapOfWorkflowRunCandidates.get(sourceGroupId)
-      if (workflowRunArray === undefined) {
-        workflowRunArray = []
-        mapOfWorkflowRunCandidates.set(sourceGroupId, workflowRunArray)
-      }
-      core.info(
-        `The candidate ${runItem.id} has been added to ${sourceGroupId} group of candidates`
-      )
-      workflowRunArray.push(runItem)
-    }
+    await checkCandidateForCancelling(
+      repositoryInfo,
+      runItem,
+      triggeringRunInfo,
+      cancelMode,
+      cancelFutureDuplicates,
+      jobNameRegexps,
+      skipEventTypes,
+      mapOfWorkflowRunCandidates
+    )
   }
   return mapOfWorkflowRunCandidates
 }
@@ -749,7 +855,7 @@ async function addCommentToPullRequest(
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - my own run id
  * @param pullRequestNumber - number of pull request
- * @param reason reason for canceling
+ * @param reason reason for cancelling
  */
 async function notifyPR(
   repositoryInfo: RepositoryInfo,
@@ -774,9 +880,9 @@ async function notifyPR(
  * @param notifyPRCancel - whether to notify the PR when cancelling
  * @param selfRunId - what is the self run id
  * @param sourceGroupId - what is the source group id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
  */
-async function cancelAllRunsInTheSourceGroup(
+async function cancelAllRunsInTheGroup(
   repositoryInfo: RepositoryInfo,
   sortedRunItems: rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[],
   notifyPRCancel: boolean,
@@ -813,21 +919,21 @@ async function cancelAllRunsInTheSourceGroup(
 }
 
 /**
- * Cancels found runs in a smart way. It takes all the found runs group by the source group, sorts them
- * descending according to create date in each of the source groups and cancels them in that order -
- * optionally skipping the first found run in each source group in case of duplicates.
+ * Cancels found runs per group. It takes all the found groups, sorts them
+ * descending according to create date in each of the groups and cancels them in that order -
+ * optionally skipping the first found run in each source group in case of duplicate cancelling.
  *
  * @param repositoryInfo - information about the repository used
- * @param mapOfWorkflowRunsCandidatesToCancel map of all workflow run candidates
+ * @param mapOfWorkflowRunCandidatesCandidatesToCancel map of all workflow run candidates
  * @param cancelMode - cancel mode
  * @param cancelFutureDuplicates - whether to cancel future duplicates
  * @param notifyPRCancel - whether to notify PRs with comments
  * @param selfRunId - self run Id
- * @param reason - reason for canceling
+ * @param reason - reason for cancelling
  */
 async function cancelTheRunsPerGroup(
   repositoryInfo: RepositoryInfo,
-  mapOfWorkflowRunsCandidatesToCancel: Map<
+  mapOfWorkflowRunCandidatesCandidatesToCancel: Map<
     string,
     rest.ActionsListWorkflowRunsResponseWorkflowRunsItem[]
   >,
@@ -839,9 +945,9 @@ async function cancelTheRunsPerGroup(
 ): Promise<number[]> {
   const cancelledRuns: number[] = []
   for (const [
-    sourceGroupId,
+    groupId,
     candidatesArray
-  ] of mapOfWorkflowRunsCandidatesToCancel) {
+  ] of mapOfWorkflowRunCandidatesCandidatesToCancel) {
     // Sort from most recent date - this way we always kill current one at the end (if we kill it at all)
     const sortedRunItems = candidatesArray.sort((runItem1, runItem2) =>
       runItem2.created_at.localeCompare(runItem1.created_at)
@@ -849,31 +955,32 @@ async function cancelTheRunsPerGroup(
     if (sortedRunItems.length > 0) {
       if (
         (cancelMode === CancelMode.DUPLICATES && cancelFutureDuplicates) ||
-        cancelMode === CancelMode.ALL_DUPLICATES
+        cancelMode === CancelMode.ALL_DUPLICATES ||
+        cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS
       ) {
         core.info(
           `\nSkipping the first run (${sortedRunItems[0].id}) of all the matching ` +
-            `duplicates for '${sourceGroupId}'. This one we are going to leave in peace!\n`
+            `duplicates for '${groupId}'. This one we are going to leave in peace!\n`
         )
         sortedRunItems.shift()
       }
       if (sortedRunItems.length === 0) {
-        core.info(`\nNo duplicates to cancel for ${sourceGroupId}!\n`)
+        core.info(`\nNo duplicates to cancel for ${groupId}!\n`)
         continue
       }
       cancelledRuns.push(
-        ...(await cancelAllRunsInTheSourceGroup(
+        ...(await cancelAllRunsInTheGroup(
           repositoryInfo,
           sortedRunItems,
           notifyPRCancel,
           selfRunId,
-          sourceGroupId,
+          groupId,
           reason
         ))
       )
     } else {
       core.info(
-        `\n######  There are no runs to cancel for ${sourceGroupId} ##########\n`
+        `\n######  There are no runs to cancel for ${groupId} ##########\n`
       )
     }
   }
@@ -884,14 +991,7 @@ async function cancelTheRunsPerGroup(
  * Find and cancels runs based on the criteria chosen.
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- *        (different than self event name for workflow_run)
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -904,11 +1004,7 @@ async function cancelTheRunsPerGroup(
 async function findAndCancelRuns(
   repositoryInfo: RepositoryInfo,
   selfRunId: number,
-  sourceWorkflowId: number | string,
-  sourceRunId: number,
-  headRepo: string,
-  headBranch: string,
-  sourceEventName: string,
+  triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
   notifyPRCancel: boolean,
@@ -918,34 +1014,24 @@ async function findAndCancelRuns(
   reason: string
 ): Promise<number[]> {
   const statusValues = ['queued', 'in_progress']
-  const mySourceGroup: WorkflowRunSourceGroup = {
-    headBranch,
-    headRepo,
-    eventName: sourceEventName,
-    workflowId: sourceWorkflowId
-  }
   const workflowRuns = await getWorkflowRunsMatchingCriteria(
     repositoryInfo,
     statusValues,
     cancelMode,
-    sourceWorkflowId,
-    sourceRunId,
-    sourceEventName,
-    mySourceGroup
+    triggeringRunInfo
   )
-  const mapOfWorkflowRunsCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
+  const mapOfWorkflowRunCandidatesCandidatesToCancel = await filterAndMapWorkflowRunsToGroups(
     repositoryInfo,
-    headRepo,
+    triggeringRunInfo,
     cancelMode,
     cancelFutureDuplicates,
-    sourceRunId,
     jobNameRegexps,
     skipEventTypes,
     workflowRuns
   )
   return await cancelTheRunsPerGroup(
     repositoryInfo,
-    mapOfWorkflowRunsCandidatesToCancel,
+    mapOfWorkflowRunCandidatesCandidatesToCancel,
     cancelMode,
     cancelFutureDuplicates,
     notifyPRCancel,
@@ -969,15 +1055,15 @@ function getRequiredEnv(key: string): string {
 }
 
 /**
- * Gets origin of the runId - if this is a workflow run, it returns the information about the source run
+ * Gets source run using  of the runId - if this is a workflow run, it returns the information about the source run
  * @param repositoryInfo - information about the repository used
  * @param runId - run id of the run to check
- * @return information about the triggering workflow
+ * @return information about the triggering run
  */
-async function getOrigin(
+async function getTriggeringRunInfo(
   repositoryInfo: RepositoryInfo,
   runId: number
-): Promise<TriggeringWorkflowInfo> {
+): Promise<TriggeringRunInfo> {
   const reply = await repositoryInfo.octokit.actions.getWorkflowRun({
     owner: repositoryInfo.owner,
     repo: repositoryInfo.repo,
@@ -1001,6 +1087,8 @@ async function getOrigin(
   }
 
   return {
+    workflowId: retrieveWorkflowIdFromUrl(reply.data.workflow_url),
+    runId,
     headRepo: reply.data.head_repository.full_name,
     headBranch: reply.data.head_branch,
     headSha: reply.data.head_sha,
@@ -1015,14 +1103,7 @@ async function getOrigin(
  *
  * @param repositoryInfo - information about the repository used
  * @param selfRunId - number of own run id
- * @param sourceWorkflowId - source workflow id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param sourceRunId - source run id that triggered the workflow
- *        (might be different than self for workflow_run)
- * @param headRepo - head repository that triggered the workflow (repo from which PR came)
- * @param headBranch - branch of the PR that triggered the workflow (when it is triggered by PR)
- * @param sourceEventName - name of the event that triggered the workflow
- *        (different than self event name for workflow_run)
+ * @param triggeringRunInfo - information about the workflow that triggered the run
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether to cancel future duplicates for duplicate cancelling
  * @param notifyPRCancel - whether to notify in PRs about cancelling
@@ -1035,11 +1116,7 @@ async function getOrigin(
 async function performCancelJob(
   repositoryInfo: RepositoryInfo,
   selfRunId: number,
-  sourceWorkflowId: number | string,
-  sourceRunId: number,
-  headRepo: string,
-  headBranch: string,
-  sourceEventName: string,
+  triggeringRunInfo: TriggeringRunInfo,
   cancelMode: CancelMode,
   notifyPRCancel: boolean,
   notifyPRCancelMessage: string,
@@ -1052,9 +1129,11 @@ async function performCancelJob(
     '\n###################################################################################\n'
   )
   core.info(
-    `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, run id: ${sourceRunId}, ` +
-      `head repo ${headRepo}, headBranch: ${headBranch}, ` +
-      `sourceEventName: ${sourceEventName}, cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
+    `All parameters: owner: ${repositoryInfo.owner}, repo: ${repositoryInfo.repo}, ` +
+      `run id: ${triggeringRunInfo.runId}, ` +
+      `head repo ${triggeringRunInfo.headRepo}, headBranch: ${triggeringRunInfo.headBranch}, ` +
+      `sourceEventName: ${triggeringRunInfo.eventName}, ` +
+      `cancelMode: ${cancelMode}, jobNames: ${jobNameRegexps}`
   )
   core.info(
     '\n###################################################################################\n'
@@ -1062,33 +1141,42 @@ async function performCancelJob(
   let reason = ''
   if (cancelMode === CancelMode.SELF) {
     core.info(
-      `# Cancelling source run: ${sourceRunId} for workflow ${sourceWorkflowId}.`
+      `# Cancelling source run: ${triggeringRunInfo.runId} ` +
+        `for workflow ${triggeringRunInfo.workflowId}.`
     )
     reason = notifyPRCancelMessage
       ? notifyPRCancelMessage
       : `The job has been cancelled by another workflow.`
   } else if (cancelMode === CancelMode.FAILED_JOBS) {
     core.info(
-      `# Cancel all runs for workflow ${sourceWorkflowId} where job names matching ${jobNameRegexps} failed.`
+      `# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+        `where job names matching ${jobNameRegexps} failed.`
     )
     reason = `It has some failed jobs matching ${jobNameRegexps}.`
   } else if (cancelMode === CancelMode.NAMED_JOBS) {
     core.info(
-      `# Cancel all runs for workflow ${sourceWorkflowId} have job names matching ${jobNameRegexps}.`
+      `# Cancel all runs for workflow ${triggeringRunInfo.workflowId} ` +
+        `have job names matching ${jobNameRegexps}.`
     )
     reason = `It has jobs matching ${jobNameRegexps}.`
   } else if (cancelMode === CancelMode.DUPLICATES) {
     core.info(
-      `# Cancel duplicate runs for workflow ${sourceWorkflowId} for same triggering branch as own run Id.`
+      `# Cancel duplicate runs for workflow ${triggeringRunInfo.workflowId} ` +
+        `for same triggering branch as own run Id.`
     )
-    reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
+    reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
   } else if (cancelMode === CancelMode.ALL_DUPLICATES) {
     core.info(
-      `# Cancel all duplicates runs started for workflow ${sourceWorkflowId}.`
+      `# Cancel all duplicates runs started for workflow ${triggeringRunInfo.workflowId}.`
+    )
+    reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
+  } else if (cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) {
+    core.info(
+      `# Cancel all duplicate named jobs matching the patterns ${jobNameRegexps}.`
     )
-    reason = `It is an earlier duplicate of ${sourceWorkflowId} run.`
+    reason = `It is an earlier duplicate of ${triggeringRunInfo.workflowId} run.`
   } else {
-    throw Error(`Wrong cancel mode ${cancelMode}! This should never happen.`)
+    throw Error(`Wrong cancel mode ${cancelMode}! Please correct it.`)
   }
   core.info(
     '\n###################################################################################\n'
@@ -1097,11 +1185,7 @@ async function performCancelJob(
   return await findAndCancelRuns(
     repositoryInfo,
     selfRunId,
-    sourceWorkflowId,
-    sourceRunId,
-    headRepo,
-    headBranch,
-    sourceEventName,
+    triggeringRunInfo,
     cancelMode,
     cancelFutureDuplicates,
     notifyPRCancel,
@@ -1119,31 +1203,31 @@ async function performCancelJob(
  *
  * @param repositoryInfo - information about the repository used
  * @param workflowFileName - optional workflow file name
- * @param sourceRunId - source run id of the workfow
+ * @param runId - run id of the workflow
  * @param selfRunId - self run id
  * @return workflow id that is associate with the workflow we are going to act on.
  */
 async function retrieveWorkflowId(
   repositoryInfo: RepositoryInfo,
   workflowFileName: string | null,
-  sourceRunId: number,
+  runId: number,
   selfRunId: number
 ): Promise<string | number> {
-  let sourceWorkflowId
+  let workflowId
   if (workflowFileName) {
-    sourceWorkflowId = workflowFileName
+    workflowId = workflowFileName
     core.info(
-      `\nFinding runs for another workflow found by ${workflowFileName} name: ${sourceWorkflowId}\n`
+      `\nFinding runs for another workflow found by ${workflowFileName} name: ${workflowId}\n`
     )
   } else {
-    sourceWorkflowId = await getWorkflowId(repositoryInfo, sourceRunId)
-    if (sourceRunId === selfRunId) {
-      core.info(`\nFinding runs for my own workflow ${sourceWorkflowId}\n`)
+    workflowId = await getWorkflowId(repositoryInfo, runId)
+    if (runId === selfRunId) {
+      core.info(`\nFinding runs for my own workflow ${workflowId}\n`)
     } else {
-      core.info(`\nFinding runs for source workflow ${sourceWorkflowId}\n`)
+      core.info(`\nFinding runs for source workflow ${workflowId}\n`)
     }
   }
-  return sourceWorkflowId
+  return workflowId
 }
 /**
  * Sets output but also prints the output value in the logs.
@@ -1161,7 +1245,7 @@ function verboseOutput(name: string, value: string): void {
  * are verified and in case od unexpected combination found, approrpriate error is raised.
  *
  * @param eventName - name of the event to act on
- * @param sourceRunId - run id of the triggering event
+ * @param runId - run id of the triggering event
  * @param selfRunId - our own run id
  * @param cancelMode - cancel mode used
  * @param cancelFutureDuplicates - whether future duplicate cancelling is enabled
@@ -1169,7 +1253,7 @@ function verboseOutput(name: string, value: string): void {
  */
 function performSanityChecks(
   eventName: string,
-  sourceRunId: number,
+  runId: number,
   selfRunId: number,
   cancelMode: CancelMode,
   cancelFutureDuplicates: boolean,
@@ -1177,7 +1261,7 @@ function performSanityChecks(
 ): void {
   if (
     eventName === 'workflow_run' &&
-    sourceRunId === selfRunId &&
+    runId === selfRunId &&
     cancelMode === CancelMode.DUPLICATES
   ) {
     throw Error(
@@ -1195,10 +1279,27 @@ function performSanityChecks(
       CancelMode.ALL_DUPLICATES
     ].includes(cancelMode)
   ) {
-    throw Error(`You cannot specify jobNames on ${cancelMode} cancelMode.`)
+    throw Error(
+      `You cannot specify jobNamesRegexps on ${cancelMode} cancelMode.`
+    )
+  }
+
+  if (
+    jobNameRegexps.length === 0 &&
+    [
+      CancelMode.NAMED_JOBS,
+      CancelMode.FAILED_JOBS,
+      CancelMode.ALL_DUPLICATED_NAMED_JOBS
+    ].includes(cancelMode)
+  ) {
+    throw Error(`You must specify jobNamesRegexps on ${cancelMode} cancelMode.`)
   }
 
-  if (cancelMode === CancelMode.ALL_DUPLICATES && !cancelFutureDuplicates) {
+  if (
+    (cancelMode === CancelMode.ALL_DUPLICATES ||
+      cancelMode === CancelMode.ALL_DUPLICATED_NAMED_JOBS) &&
+    !cancelFutureDuplicates
+  ) {
     throw Error(
       `The  ${cancelMode} cancelMode has to have cancelFutureDuplicates set to true.`
     )
@@ -1209,32 +1310,28 @@ function performSanityChecks(
  * Produces basic outputs for the action. This does not include cancelled workflow run id - those are
  * set after cancelling is done.
  *
- * @param triggeringWorkflowInfo
+ * @param triggeringRunInfo
  */
-function produceBasicOutputs(
-  triggeringWorkflowInfo: TriggeringWorkflowInfo
-): void {
-  verboseOutput('sourceHeadRepo', triggeringWorkflowInfo.headRepo)
-  verboseOutput('sourceHeadBranch', triggeringWorkflowInfo.headBranch)
-  verboseOutput('sourceHeadSha', triggeringWorkflowInfo.headSha)
-  verboseOutput('sourceEvent', triggeringWorkflowInfo.eventName)
+function produceBasicOutputs(triggeringRunInfo: TriggeringRunInfo): void {
+  verboseOutput('sourceHeadRepo', triggeringRunInfo.headRepo)
+  verboseOutput('sourceHeadBranch', triggeringRunInfo.headBranch)
+  verboseOutput('sourceHeadSha', triggeringRunInfo.headSha)
+  verboseOutput('sourceEvent', triggeringRunInfo.eventName)
   verboseOutput(
     'pullRequestNumber',
-    triggeringWorkflowInfo.pullRequest
-      ? triggeringWorkflowInfo.pullRequest.number.toString()
+    triggeringRunInfo.pullRequest
+      ? triggeringRunInfo.pullRequest.number.toString()
       : ''
   )
   verboseOutput(
     'mergeCommitSha',
-    triggeringWorkflowInfo.mergeCommitSha
-      ? triggeringWorkflowInfo.mergeCommitSha
-      : ''
+    triggeringRunInfo.mergeCommitSha ? triggeringRunInfo.mergeCommitSha : ''
   )
   verboseOutput(
     'targetCommitSha',
-    triggeringWorkflowInfo.mergeCommitSha
-      ? triggeringWorkflowInfo.mergeCommitSha
-      : triggeringWorkflowInfo.headSha
+    triggeringRunInfo.mergeCommitSha
+      ? triggeringRunInfo.mergeCommitSha
+      : triggeringRunInfo.headSha
   )
 }
 
@@ -1242,19 +1339,17 @@ function produceBasicOutputs(
  * Notifies the PR that the action has started.
  *
  * @param repositoryInfo information about the repository
- * @param triggeringWorkflowInfo information about the triggering workflow
- * @param sourceRunId run id of the source workflow
+ * @param triggeringRunInfo information about the triggering workflow
  * @param selfRunId self run id
  * @param notifyPRMessageStart whether to notify about the start of the action
  */
 async function notifyActionStart(
   repositoryInfo: RepositoryInfo,
-  triggeringWorkflowInfo: TriggeringWorkflowInfo,
-  sourceRunId: number,
+  triggeringRunInfo: TriggeringRunInfo,
   selfRunId: number,
   notifyPRMessageStart: string
 ): Promise<void> {
-  if (notifyPRMessageStart && triggeringWorkflowInfo.pullRequest) {
+  if (notifyPRMessageStart && triggeringRunInfo.pullRequest) {
     const selfWorkflowRunUrl =
       `https://github.com/${repositoryInfo.owner}/${repositoryInfo.repo}` +
       `/actions/runs/${selfRunId}`
@@ -1262,7 +1357,7 @@ async function notifyActionStart(
       owner: repositoryInfo.owner,
       repo: repositoryInfo.repo,
       // eslint-disable-next-line @typescript-eslint/camelcase
-      issue_number: triggeringWorkflowInfo.pullRequest.number,
+      issue_number: triggeringRunInfo.pullRequest.number,
       body: `${notifyPRMessageStart} [The workflow run](${selfWorkflowRunUrl})`
     })
   }
@@ -1331,13 +1426,15 @@ async function run(): Promise<void> {
       `jobNames: ${jobNameRegexps}`
   )
 
-  const triggeringWorkflowInfo = await getOrigin(repositoryInfo, sourceRunId)
-  produceBasicOutputs(triggeringWorkflowInfo)
+  const triggeringRunInfo = await getTriggeringRunInfo(
+    repositoryInfo,
+    sourceRunId
+  )
+  produceBasicOutputs(triggeringRunInfo)
 
   await notifyActionStart(
     repositoryInfo,
-    triggeringWorkflowInfo,
-    sourceRunId,
+    triggeringRunInfo,
     selfRunId,
     notifyPRMessageStart
   )
@@ -1345,11 +1442,7 @@ async function run(): Promise<void> {
   const cancelledRuns = await performCancelJob(
     repositoryInfo,
     selfRunId,
-    sourceWorkflowId,
-    sourceRunId,
-    triggeringWorkflowInfo.headRepo,
-    triggeringWorkflowInfo.headBranch,
-    triggeringWorkflowInfo.eventName,
+    triggeringRunInfo,
     cancelMode,
     notifyPRCancel,
     notifyPRCancelMessage,


[airflow-cancel-workflow-runs] 18/44: realease

Posted by po...@apache.org.
This is an automated email from the ASF dual-hosted git repository.

potiuk pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/airflow-cancel-workflow-runs.git

commit 953c92201f368370112ea2754545cb4468d89f12
Author: Jason T. Greene <ja...@redhat.com>
AuthorDate: Wed Feb 19 15:16:11 2020 -0600

    realease
---
 action.yml | 2 +-
 1 file changed, 1 insertion(+), 1 deletion(-)

diff --git a/action.yml b/action.yml
index d832731..79df71e 100644
--- a/action.yml
+++ b/action.yml
@@ -1,4 +1,4 @@
-name: 'Cancel Previous Runs'
+name: 'Cancel Previous Workflow Runs'
 description: 'Cancels all previous runs of this workflow'
 author: 'n1hility'
 inputs: