Repository: stoplightio/spectral
Pull request #1134 (2020-04-30 07:52:39)

chore: export functions to help Spectral integration
Proposed changes:
- Both `vscode-spectral` and `spectral-action` need to register the supported format. Rather than forcing them to copy/paste the Spectral inner code, let's export an array of supported formats/format validators.
- Exporting the `severeEnoughToFail` would allow those same projects to easily surface a parameter similar to Spectral CLI own `fail-severity` without re-implementing the filtering algorithm.

**Checklist**

- [ ] Tests added / updated
- [ ] Docs added / updated

**Does this PR introduce a breaking change?**

- [ ] Yes
- [x] No



Patch:
diff --git a/.editorconfig b/.editorconfig
index e76d119d..0d010b0e 100644
--- a/.editorconfig
+++ b/.editorconfig
@@ -3,7 +3,7 @@ root = true
 [*]
 charset = utf-8
 
-[*.{ts,js,yaml,scenario,md}]
+[*.{ts,js,yaml,yml,json,scenario,md}]
 indent_style = space
 indent_size = 2
 
diff --git a/README.md b/README.md
index 0f12356e..3897b993 100644
--- a/README.md
+++ b/README.md
@@ -67,7 +67,7 @@ If you are interested in contributing to Spectral, check out [CONTRIBUTING.md](C
 - [Spectral GitHub Action](https://github.com/stoplightio/spectral-action), lints documents in your repo, built by [Vincenzo Chianese](https://github.com/XVincentX/).
 - [VS Code Spectral](https://github.com/stoplightio/vscode-spectral), all the power of Spectral without leaving VS Code.
 
-## Licence
+## License
 
 Spectral is 100% free and open-source, under the [MIT license](LICENSE). Use it however you want.
 
diff --git a/docs/reference/functions.md b/docs/reference/functions.md
index 16323e7c..cba014d7 100644
--- a/docs/reference/functions.md
+++ b/docs/reference/functions.md
@@ -8,9 +8,9 @@ Enforce alphabetical content, for simple arrays, or for objects by passing a key
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-keyedBy | key to sort an object by | no
+| name    | description              | type     | required? |
+| ------- | ------------------------ | -------- | --------- |
+| keyedBy | key to sort an object by | `string` | no        |
 
 <!-- title: example -->
 
@@ -33,9 +33,9 @@ Does the field value exist in this set of possible values?
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-values | an array of possible values | yes
+| name   | description                 | type                   | required? |
+| ------ | --------------------------- | ---------------------- | --------- |
+| values | an array of possible values | `Array<number,string>` | yes       |
 
 <!-- title: example -->
 
@@ -83,22 +83,22 @@ Regular expressions!
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-match | if provided, value must match this regex | no
-notMatch | if provided, value must _not_ match this regex | no
+| name     | description                                    | type     | required? |
+| -------- | ---------------------------------------------- | -------- | --------- |
+| match    | if provided, value must match this regex       | `string` | no        |
+| notMatch | if provided, value must _not_ match this regex | `string` | no        |
 
 <!-- title: example -->
 
 ```yaml
-  path-no-trailing-slash:
-    description: Paths should not end with `#/`.
-    type: style
-    given: "$.paths[*]~"
-    then:
-      function: pattern
-      functionOptions:
-        notMatch: ".+\/$"
+path-no-trailing-slash:
+  description: Paths should not end with `#/`.
+  type: style
+  given: "$.paths[*]~"
+  then:
+    function: pattern
+    functionOptions:
+      notMatch: ".+\/$"
 ```
 
 ## casing
@@ -107,22 +107,23 @@ Text must match a certain case, like `camelCase` or `snake_case`.
 
 <!-- title: functionOptions -->
 
-name                   | description                                             | required?
------------------------|---------------------------------------------------------|----------
-type                   | the casing type to match against                        | yes
-disallowDigits         | if not truthy, digits are allowed                       | no
-separator.char         | additional char to separate groups of words             | no
-separator.allowLeading | can the group separator char be used at the first char? | no
+| name                   | description                                             | type         | required? |
+| ---------------------- | ------------------------------------------------------- | ------------ | --------- |
+| type                   | the casing type to match against                        | `CasingType` | yes       |
+| disallowDigits         | if not truthy, digits are allowed                       | `boolean`    | no        |
+| separator.char         | additional char to separate groups of words             | `string`     | no        |
+| separator.allowLeading | can the group separator char be used at the first char? | `boolean`    | no        |
 
 **Note:** In advanced scenarios, `separator.char` and `separator.allowLeading` can be leveraged to validate certain naming conventions.
 For instance, the following naming style could be enforced:
- - Headers _(eg. `X-YourMighty-Header`)_: type: `pascal`, separator.char: `-`
- - Camel cased paths _(eg. `/path/toThe/amazingResource`)_: type: `camel`, separator.char: `/`, separator.allowLeading: `true`
+
+- Headers _(eg. `X-YourMighty-Header`)_: type: `pascal`, separator.char: `-`
+- Camel cased paths _(eg. `/path/toThe/amazingResource`)_: type: `camel`, separator.char: `/`, separator.allowLeading: `true`
 
 Available types are:
 
 | name   | sample         |
-|--------|----------------|
+| ------ | -------------- |
 | flat   | verylongname   |
 | camel  | veryLongName   |
 | pascal | VeryLongName   |
@@ -146,14 +147,14 @@ camel-case-name:
 
 ## schema
 
-Use JSON Schema (draft 4, 6 or 7) to treat the contents of the $given JSON Path as a JSON instance.
+Use JSON Schema (draft 4, 6 or 7) to treat the contents of the \$given JSON Path as a JSON instance.
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-schema | a valid JSON Schema document | yes
-allErrors | returns all errors when `true`; otherwise only returns the first error | no
+| name      | description                                                            | type         | required? |
+| --------- | ---------------------------------------------------------------------- | ------------ | --------- |
+| schema    | a valid JSON Schema document                                           | `JSONSchema` | yes       |
+| allErrors | returns all errors when `true`; otherwise only returns the first error | `boolean`    | no        |
 
 <!-- title: example -->
 
@@ -180,27 +181,27 @@ The schema-path rule is very meta. It is an extension of the schema rule, but it
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-field | the field to check | yes
-schemaPath | a json path pointing to the json schema to use | yes
-allErrors | returns all errors when `true`; otherwise only returns the first error | no
+| name       | description                                                            | type      | required? |
+| ---------- | ---------------------------------------------------------------------- | --------- | --------- |
+| field      | the field to check                                                     | `string`  | yes       |
+| schemaPath | a json path pointing to the json schema to use                         | `string`  | yes       |
+| allErrors  | returns all errors when `true`; otherwise only returns the first error | `boolean` | no        |
 
 <!-- title: example -->
 
-``` yaml
-  valid-oas-example-in-parameters:
-    description: Examples must be valid against their defined schema.
-    message: "{{error}}"
-    recommended: true
-    severity: 0
-    type: validation
-    given: "$..parameters..[?(@.example && @.schema)]"
-    then:
-      function: schemaPath
-      functionOptions:
-        field: example
-        schemaPath: "$.schema"
+```yaml
+valid-oas-example-in-parameters:
+  description: Examples must be valid against their defined schema.
+  message: "{{error}}"
+  recommended: true
+  severity: 0
+  type: validation
+  given: "$..parameters..[?(@.example && @.schema)]"
+  then:
+    function: schemaPath
+    functionOptions:
+      field: example
+      schemaPath: "$.schema"
 ```
 
 ## truthy
@@ -209,7 +210,7 @@ The value should not be `false`, `""`, `0`, `null` or `undefined`. Basically any
 
 <!-- title: example -->
 
-``` yaml
+```yaml
 important-fields:
   description: Absolutely must have a title and a description
   message: "Missing the {{property}}"
@@ -226,7 +227,7 @@ important-fields:
 
 The value must be `undefined`. When combined with `field: foo` on an object the `foo` property must be undefined.
 
-_**Note:** Due to the way YAML works, just having `foo: ` with no value set is not the same as being `undefined`. This would be `falsy`._
+_**Note:** Due to the way YAML works, just having `foo:` with no value set is not the same as being `undefined`. This would be `falsy`._
 
 ## unreferencedReusableObject
 
@@ -238,13 +239,13 @@ _Warning:_ This function may identify false positives when used against a specif
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-reusableObjectsLocation | a local json pointer to the document member holding the reusable objects (eg. `#/definitions` for an OAS2 document, `#/components/schemas` for an OAS3 document). | yes
+| name                    | description                                                                                                                                                       | type     | required? |
+| ----------------------- | ----------------------------------------------------------------------------------------------------------------------------------------------------------------- | -------- | --------- |
+| reusableObjectsLocation | a local json pointer to the document member holding the reusable objects (eg. `#/definitions` for an OAS2 document, `#/components/schemas` for an OAS3 document). | `string` | yes       |
 
 <!-- title: example -->
 
-``` yaml
+```yaml
 unused-definition:
   description: Potentially unused definition has been detected.
   recommended: true
@@ -263,9 +264,9 @@ Communicate that one of these properties is required, and no more than one is al
 
 <!-- title: functionOptions -->
 
-name | description | required?
----------|----------|---------
-properties | the properties to check | yes
+| name       | description             | type       | required? |
+| ---------- | ----------------------- | ---------- | --------- |
+| properties | the properties to check | `string[]` | yes       |
 
 <!-- title: example -->
 
@@ -278,8 +279,8 @@ components-examples-value-or-externalValue:
     function: xor
     functionOptions:
       properties:
-      - externalValue
-      - value
+        - externalValue
+        - value
 ```
 
 ## typedEnum
diff --git a/karma-jest.ts b/karma-jest.ts
index 3fe2bbd9..9d5f6568 100644
--- a/karma-jest.ts
+++ b/karma-jest.ts
@@ -29,3 +29,6 @@ test.each = input => (name: string, fn: Function) => {
     }
   }
 };
+
+// @ts-ignore
+describe.each = test.each;
diff --git a/package.json b/package.json
index aaa0f2fd..693ff64e 100644
--- a/package.json
+++ b/package.json
@@ -77,7 +77,6 @@
     "lodash": "4.17.15",
     "nanoid": "2.1.11",
     "node-fetch": "2.6",
-    "promise.allsettled": "1.0.2",
     "proxy-agent": "3.1.1",
     "strip-ansi": "6.0",
     "text-table": "0.2",
@@ -99,31 +98,31 @@
     "@types/node": "~13.1",
     "@types/node-fetch": "^2.5.3",
     "@types/text-table": "^0.2.1",
-    "@types/tmp": "^0.1.0",
+    "@types/tmp": "^0.2.0",
     "@types/xml2js": "^0.4.5",
     "copyfiles": "^2.2.0",
     "dependency-graph": "^0.8.0",
     "fetch-mock": "^7.7.3",
     "glob-fs": "^0.1.7",
     "husky": "^4.2.5",
-    "jest": "~25.4",
-    "jest-mock": "^25.4.0",
+    "jest": "~25.5",
+    "jest-mock": "^26.0.1",
     "jest-when": "~2.7",
     "karma": "^4.4.1",
     "karma-chrome-launcher": "^3.1.0",
     "karma-jasmine": "^3.1.1",
     "karma-typescript": "^5.0.2",
-    "lint-staged": "^10.1.7",
+    "lint-staged": "^10.2.2",
     "nock": "~12.0.2",
     "node-html-parser": "^1.2.7",
     "pkg": "^4.4.8",
     "recast": "^0.18.5",
     "rimraf": "^3.0.2",
-    "rollup": "^2.7.2",
+    "rollup": "^2.10.0",
     "rollup-plugin-terser": "^5.3.0",
     "rollup-plugin-typescript2": "^0.27.0",
     "ts-jest": "^25.0.0",
-    "ts-node": "^8.9.1",
+    "ts-node": "^8.10.1",
     "tsconfig-paths": "^3.9.0",
     "tslint": "~5.20",
     "tslint-config-stoplight": "~1.4",
diff --git a/src/cli/commands/lint.ts b/src/cli/commands/lint.ts
index 7e101a3f..51978780 100644
--- a/src/cli/commands/lint.ts
+++ b/src/cli/commands/lint.ts
@@ -178,7 +178,7 @@ const filterResultsBySeverity = (results: IRuleResult[], failSeverity: FailSever
   return results.filter(r => r.severity <= diagnosticSeverity);
 };
 
-const severeEnoughToFail = (results: IRuleResult[], failSeverity: FailSeverity): boolean => {
+export const severeEnoughToFail = (results: IRuleResult[], failSeverity: FailSeverity): boolean => {
   const diagnosticSeverity = getDiagnosticSeverity(failSeverity);
   return results.some(r => r.severity <= diagnosticSeverity);
 };
diff --git a/src/cli/services/linter/linter.ts b/src/cli/services/linter/linter.ts
index e0473d3a..cc23861a 100644
--- a/src/cli/services/linter/linter.ts
+++ b/src/cli/services/linter/linter.ts
@@ -12,7 +12,6 @@ import {
 } from '../../../formats';
 import { readParsable } from '../../../fs/reader';
 import * as Parsers from '../../../parsers';
-import { isRuleEnabled } from '../../../runner';
 import { IRuleResult, Spectral } from '../../../spectral';
 import { FormatLookup } from '../../../types';
 import { ILintConfig } from '../../../types/config';
@@ -31,6 +30,8 @@ const KNOWN_FORMATS: Array<[string, FormatLookup, string]> = [
   ['json-schema-2019-09', isJSONSchemaDraft2019_09, 'JSON Schema Draft 2019-09 detected'],
 ];
 
+export const BUILTIN_FORMATS: Array<[string, FormatLookup]> = KNOWN_FORMATS.map(kf => [kf[0], kf[1]]);
+
 export async function lint(documents: Array<number | string>, flags: ILintConfig) {
   const spectral = new Spectral({
     resolver: getResolver(flags.resolver),
@@ -56,7 +57,7 @@ export async function lint(documents: Array<number | string>, flags: ILintConfig
   if (flags.verbose) {
     if (ruleset) {
       const rules = Object.values(spectral.rules);
-      console.info(`Found ${rules.length} rules (${rules.filter(isRuleEnabled).length} enabled)`);
+      console.info(`Found ${rules.length} rules (${rules.filter(rule => rule.enabled).length} enabled)`);
     } else {
       console.info('No rules loaded, attempting to detect document type');
     }
diff --git a/src/functions/alphabetical.ts b/src/functions/alphabetical.ts
index cbc810ee..81a2e412 100644
--- a/src/functions/alphabetical.ts
+++ b/src/functions/alphabetical.ts
@@ -28,7 +28,7 @@ const getUnsortedItems = <T>(arr: T[], compareFn: (a: T, B: T) => number): null
   return null;
 };
 
-export const alphabetical: IFunction<IAlphaRuleOptions> = (targetVal, opts, paths, { documentInventory }) => {
+export const alphabetical: IFunction<IAlphaRuleOptions | null> = (targetVal, opts, paths, { documentInventory }) => {
   const results: IFunctionResult[] = [];
 
   if (!isObject(targetVal)) {
@@ -49,7 +49,7 @@ export const alphabetical: IFunction<IAlphaRuleOptions> = (targetVal, opts, path
     return results;
   }
 
-  const { keyedBy } = opts;
+  const keyedBy = opts?.keyedBy;
 
   const unsortedItems = getUnsortedItems<unknown>(
     targetArray,
diff --git a/src/functions/casing.ts b/src/functions/casing.ts
index 7f8d15a9..fffe480a 100644
--- a/src/functions/casing.ts
+++ b/src/functions/casing.ts
@@ -1,5 +1,4 @@
 import { Dictionary } from '@stoplight/types';
-import { AssertionError } from 'assert';
 import { escapeRegExp } from 'lodash';
 import { IFunction } from '../types';
 
@@ -37,8 +36,6 @@ export const casing: IFunction<ICasingOptions> = (targetVal, opts) => {
     return;
   }
 
-  assertValidOptions(opts);
-
   const casingValidator = buildFrom(CASES[opts.type], opts);
 
   if (casingValidator.test(targetVal)) {
@@ -52,26 +49,6 @@ export const casing: IFunction<ICasingOptions> = (targetVal, opts) => {
   ];
 };
 
-function assertValidOptions(opts: ICasingOptions): asserts opts is ICasingOptions {
-  if (!(opts.type in CASES)) {
-    throw new AssertionError({ message: `Invalid '${opts.type}' type value.` });
-  }
-
-  if (opts.separator === undefined) {
-    return;
-  }
-
-  if (opts.separator.allowLeading !== undefined && opts.separator.char === undefined) {
-    throw new AssertionError({
-      message: "'separator.allowLeading' can only be valued when 'separator.char' is defined.",
-    });
-  }
-
-  if (opts.separator.char.length !== 1) {
-    throw new AssertionError({ message: "When valued, 'separator.char' should only be one character long." });
-  }
-}
-
 const DIGITS_PATTERN = '0-9';
 
 const buildFrom = (basePattern: string, overrides: ICasingOptions): RegExp => {
diff --git a/src/functions/schema-path.ts b/src/functions/schema-path.ts
index 66a279d6..e665d8f4 100644
--- a/src/functions/schema-path.ts
+++ b/src/functions/schema-path.ts
@@ -11,8 +11,8 @@ import { JSONPath } from 'jsonpath-plus';
 
 import { Optional } from '@stoplight/types';
 
+import { getLintTargets } from '../runner/utils/getLintTargets';
 import { IFunction, IFunctionResult } from '../types';
-import { getLintTargets } from '../utils';
 import { schema } from './schema';
 
 export interface ISchemaPathOptions {
@@ -20,7 +20,7 @@ export interface ISchemaPathOptions {
   // the `path.to.prop` to field, or special `@key` value to target keys for matched `given` object
   field?: string;
   // The oasVersion, either 2 or 3 for OpenAPI Spec versions, could also be 3.1 or a larger number if there's a need for it, otherwise JSON Schema
-  oasVersion?: Optional<number>;
+  oasVersion?: Optional<2 | 3 | 3.1>;
   allErrors?: boolean;
 }
 
diff --git a/src/functions/schema.ts b/src/functions/schema.ts
index 8a9db9f6..5574d70e 100644
--- a/src/functions/schema.ts
+++ b/src/functions/schema.ts
@@ -24,7 +24,7 @@ export interface ISchemaFunction extends IFunction<ISchemaOptions> {
 export interface ISchemaOptions {
   schema: object;
   // The oasVersion, either 2 or 3 for OpenAPI Spec versions, could also be 3.1 or a larger number if there's a need for it, otherwise JSON Schema
-  oasVersion?: Optional<number>;
+  oasVersion?: Optional<2 | 3 | 3.1>;
   allErrors?: boolean;
   ajv?: ValidateFunction;
 
diff --git a/src/index.ts b/src/index.ts
index a7fd476c..9969157d 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -2,5 +2,6 @@ import * as Parsers from './parsers';
 
 export * from './spectral';
 export * from './formats';
+export { Rule } from './rule';
 export { Parsers };
 export { Document, ParsedDocument } from './document';
diff --git a/src/linter.ts b/src/linter.ts
deleted file mode 100644
index dc47d335..00000000
--- a/src/linter.ts
+++ /dev/null
@@ -1,127 +0,0 @@
-import { decodePointerFragment } from '@stoplight/json';
-import { get } from 'lodash';
-
-import { JsonPath, Optional } from '@stoplight/types';
-import { Document } from './document';
-import { IMessageVars, message } from './rulesets/message';
-import { getDiagnosticSeverity } from './rulesets/severity';
-import { IRunningContext } from './runner';
-import { IGivenNode, IRuleResult, IRunRule } from './types';
-import { getClosestJsonPath, getLintTargets, printPath, PrintStyle } from './utils';
-import { IExceptionLocation } from './utils/pivotExceptions';
-
-const arePathsEqual = (one: JsonPath, another: JsonPath): boolean => {
-  if (one.length !== another.length) {
-    return false;
-  }
-
-  for (let i = 0; i < one.length; i++) {
-    if (one[i] !== another[i]) {
-      return false;
-    }
-  }
-
-  return true;
-};
-
-const isAKnownException = (
-  path: JsonPath,
-  source: Optional<string | null>,
-  locations: IExceptionLocation[],
-): boolean => {
-  for (const location of locations) {
-    if (source !== location.source) {
-      continue;
-    }
-
-    if (arePathsEqual(path, location.path)) {
-      return true;
-    }
-  }
-
-  return false;
-};
-
-export const lintNode = async (
-  context: IRunningContext,
-  node: IGivenNode,
-  rule: IRunRule,
-  exceptionLocations: Optional<IExceptionLocation[]>,
-): Promise<IRuleResult[]> => {
-  const results: IRuleResult[] = [];
-
-  for (const then of Array.isArray(rule.then) ? rule.then : [rule.then]) {
-    const func = context.functions[then.function];
-    if (typeof func !== 'function') {
-      throw new ReferenceError(`Function ${then.function} not found. Called by rule ${rule.name}.`);
-    }
-
-    const givenPath = node.path[0] === '$' ? node.path.slice(1) : node.path;
-    const targets = getLintTargets(node.value, then.field);
-
-    for (const target of targets) {
-      const targetPath = [...givenPath, ...target.path];
-
-      const targetResults = await func(
-        target.value,
-        then.functionOptions || {},
-        {
-          given: givenPath,
-          target: targetPath,
-        },
-        {
-          original: node.value,
-          given: node.value,
-          documentInventory: context.documentInventory,
-        },
-      );
-
-      if (targetResults === void 0) continue;
-
-      for (const result of targetResults) {
-        const escapedJsonPath = (result.path || targetPath).map(segment => decodePointerFragment(String(segment)));
-        const associatedItem = context.documentInventory.findAssociatedItemForPath(
-          escapedJsonPath,
-          rule.resolved !== false,
-        );
-        const path = associatedItem?.path || getClosestJsonPath(context.documentInventory.resolved, escapedJsonPath);
-        const source = associatedItem?.document.source;
-
-        if (exceptionLocations !== void 0 && isAKnownException(path, source, exceptionLocations)) {
-          continue;
-        }
-
-        const document = associatedItem?.document || context.documentInventory.document;
-        const range = document.getRangeForJsonPath(path, true) || Document.DEFAULT_RANGE;
-        const value = path.length === 0 ? document.data : get(document.data, path);
-
-        const vars: IMessageVars = {
-          property:
-            associatedItem?.missingPropertyPath && associatedItem.missingPropertyPath.length > path.length
-              ? printPath(associatedItem.missingPropertyPath.slice(path.length - 1), PrintStyle.Dot)
-              : path.length > 0
-              ? path[path.length - 1]
-              : '',
-          error: result.message,
-          path: printPath(path, PrintStyle.EscapedPointer),
-          description: rule.description,
-          value,
-        };
-
-        const resultMessage = message(result.message, vars);
-        vars.error = resultMessage;
-
-        results.push({
-          code: rule.name,
-          message: (rule.message === void 0 ? rule.description ?? resultMessage : message(rule.message, vars)).trim(),
-          path,
-          severity: getDiagnosticSeverity(rule.severity),
-          ...(source !== null && { source }),
-          range,
-        });
-      }
-    }
-  }
-
-  return results;
-};
diff --git a/src/meta/rule.schema.json b/src/meta/rule.schema.json
index 5dbd7612..de6a1537 100644
--- a/src/meta/rule.schema.json
+++ b/src/meta/rule.schema.json
@@ -1,150 +1,464 @@
 {
-    "$id": "http://stoplight.io/schemas/rule.schema.json",
-    "$schema": "http://json-schema.org/draft-07/schema#",
-    "definitions": {
-        "IThen<T,O>": {
+  "$id": "http://stoplight.io/schemas/rule.schema.json",
+  "$schema": "http://json-schema.org/draft-07/schema#",
+  "definitions": {
+    "Then": {
+      "allOf": [
+        {
+          "properties": {
+            "field": {
+              "type": "string"
+            }
+          }
+        },
+
+        {
+          "if": {
+           "properties": {
+             "function": {
+               "const": "alphabetical"
+             }
+           }
+          },
+          "then": {
             "properties": {
-                "field": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "keyedBy": {
                     "type": "string"
+                  }
                 },
-                "function": {
-                    "$ref": "#/definitions/T"
+                "type": ["object", "null"]
+              }
+            }
+          }
+        },
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "casing"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "type": {
+                    "type": "string",
+                    "enum": ["flat", "camel", "pascal", "kebab", "cobol", "snake", "macro"]
+                  },
+                  "disallowDigits": {
+                    "type": "boolean"
+                  },
+                  "separator": {
+                    "additionalProperties": false,
+                    "properties": {
+                      "char": {
+                        "type": "string",
+                        "maxLength": 1
+                      },
+                      "allowLeading": {
+                        "type": "boolean"
+                      }
+                    },
+                    "required": ["char"],
+                    "type": "object"
+                  }
                 },
-                "functionOptions": {
-                    "$ref": "#/definitions/O"
-                }
+                "required": ["type"],
+                "type": "object"
+              }
             },
-            "required": [
-                "function"
-            ],
-            "type": "object"
-        },
-        "O": {
-            "type": "object"
+            "required": ["functionOptions"]
+          }
         },
-        "T": {
-            "type": "string"
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "enumeration"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "values": {
+                    "type": "array",
+                    "items": {
+                      "type": ["string", "number"]
+                    }
+                  }
+                },
+                "type": "object",
+                "required": ["values"]
+              }
+            },
+            "required": ["functionOptions"]
+          }
         },
-        "DiagnosticSeverity": {
-            "enum": [
-                -1,
-                0,
-                1,
-                2,
-                3
-            ],
-            "type": "number"
-        },
-        "HumanReadableSeverity": {
-            "type": "string",
-            "enum": [
-                "error",
-                "warn",
-                "info",
-                "hint",
-                "off"
-            ]
-        },
-        "Severity": {
-            "oneOf": [
-                {
-                    "$ref": "#/definitions/DiagnosticSeverity"
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "length"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "min": {
+                    "type": "number"
+                  },
+                  "max": {
+                    "type": "number"
+                  }
                 },
-                {
-                    "$ref": "#/definitions/HumanReadableSeverity"
-                }
-            ]
-        }
-    },
-    "oneOf": [
+                "minProperties": 1,
+                "type": "object"
+              }
+            },
+            "required": ["functionOptions"]
+          }
+        },
+
         {
+          "if": {
             "properties": {
-                "description": {
+              "function": {
+                "const": "pattern"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "match": {
+                    "type": "string"
+                  },
+                  "notMatch": {
                     "type": "string"
+                  }
                 },
-                "recommended": {
+                "minProperties": 1,
+                "type": "object"
+              }
+            },
+            "required": ["functionOptions"]
+          }
+        },
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "schema"
+              }
+            }
+          },
+          "then": {
+            "required": ["functionOptions"],
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "schema": {
+                    "type": "object"
+                  },
+                  "oasVersion": {
+                    "enum": [
+                      2,
+                      3,
+                      3.1
+                    ],
+                    "type": "number"
+                  },
+                  "allErrors": {
                     "type": "boolean"
+                  }
                 },
-                "given": {
-                    "oneOf": [
-                        {
-                            "type": "string"
-                        },
-                        {
-                            "type": "array",
-                            "items": {
-                                "type": "string"
-                            }
-                        }
-                    ]
-                },
-                "resolved": {
+                "required": [
+                  "schema"
+                ],
+                "type": "object"
+              }
+            }
+          }
+        },
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "schema-path"
+              }
+            }
+          },
+          "then": {
+            "required": ["functionOptions"],
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "schemaPath": {
+                    "type": "string"
+                  },
+                  "field": {
+                    "type": "string"
+                  },
+                  "oasVersion": {
+                    "enum": [
+                      2,
+                      3,
+                      3.1
+                    ],
+                    "type": "number"
+                  },
+                  "allErrors": {
                     "type": "boolean"
+                  }
                 },
-                "severity": {
-                    "$ref": "#/definitions/Severity"
-                },
-                "message": {
+                "required": [
+                  "schemaPath"
+                ],
+                "type": "object"
+              }
+            }
+          }
+        },
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "unreferencedReusableObject"
+              }
+            }
+          },
+          "then": {
+            "required": ["functionOptions"],
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "reusableObjectsLocation": {
                     "type": "string"
+                  }
                 },
-                "tags": {
-                    "items": {
-                        "type": "string"
-                    },
-                    "type": "array"
-                },
-                "formats": {
+                "required": [
+                  "reusableObjectsLocation"
+                ],
+                "type": "object"
+              }
+            }
+          }
+        },
+
+        {
+          "if": {
+            "properties": {
+              "function": {
+                "const": "xor"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "additionalProperties": false,
+                "properties": {
+                  "properties": {
                     "type": "array",
                     "items": {
-                        "type": "string",
-                        "minItems": 1
-                    }
-                },
-                "then": {
-                    "anyOf": [
-                        {
-                            "$ref": "#/definitions/IThen<T,O>"
-                        },
-                        {
-                            "items": {
-                                "$ref": "#/definitions/IThen<T,O>"
-                            },
-                            "type": "array"
-                        }
-                    ]
+                      "type": "string"
+                    },
+                    "minItems": 2
+                  }
                 },
-                "type": {
-                    "enum": [
-                        "style",
-                        "validation"
-                    ],
-                    "type": "string"
-                }
+                "type": "object",
+                "required": ["properties"]
+              }
             },
-            "required": [
-                "given",
-                "then"
-            ],
-            "type": "object"
+            "required": ["functionOptions"]
+          }
         },
+
         {
-            "$ref": "#/definitions/HumanReadableSeverity"
+          "if": {
+            "properties": {
+              "function": {
+                "type": "string",
+                "enum": [
+                  "truthy",
+                  "falsy",
+                  "undefined"
+                ]
+              }
+            }
+          },
+          "then": {
+            "type": "object",
+            "properties": {
+              "functionOptions": {
+                "type": ["null"]
+              }
+            }
+          }
         },
+
         {
-            "type": "boolean"
+          "if": {
+            "properties": {
+              "function": {
+                "type": "string"
+              }
+            }
+          },
+          "then": {
+            "properties": {
+              "functionOptions": {
+                "type": ["object", "null"]
+              }
+            }
+          }
+        }
+      ],
+      "type": "object"
+    },
+    "DiagnosticSeverity": {
+      "enum": [
+        -1,
+        0,
+        1,
+        2,
+        3
+      ],
+      "type": "number"
+    },
+    "HumanReadableSeverity": {
+      "type": "string",
+      "enum": [
+        "error",
+        "warn",
+        "info",
+        "hint",
+        "off"
+      ]
+    },
+    "Severity": {
+      "oneOf": [
+        {
+          "$ref": "#/definitions/DiagnosticSeverity"
         },
         {
-            "type": "array",
-            "additionalItems": false,
-            "minItems": 1,
-            "items": [
-                {
-                    "$ref": "#/definitions/Severity"
-                },
-                {
-                    "type": "object"
-                }
-            ]
+          "$ref": "#/definitions/HumanReadableSeverity"
+        }
+      ]
+    }
+  },
+  "oneOf": [
+    {
+      "properties": {
+        "description": {
+          "type": "string"
+        },
+        "recommended": {
+          "type": "boolean"
+        },
+        "given": {
+          "oneOf": [
+            {
+              "type": "string"
+            },
+            {
+              "type": "array",
+              "items": {
+                "type": "string"
+              }
+            }
+          ]
+        },
+        "resolved": {
+          "type": "boolean"
+        },
+        "severity": {
+          "$ref": "#/definitions/Severity"
+        },
+        "message": {
+          "type": "string"
+        },
+        "tags": {
+          "items": {
+            "type": "string"
+          },
+          "type": "array"
+        },
+        "formats": {
+          "type": "array",
+          "items": {
+            "type": "string",
+            "minItems": 1
+          }
+        },
+        "then": {
+          "anyOf": [
+            {
+              "$ref": "#/definitions/Then"
+            },
+            {
+              "items": {
+                "$ref": "#/definitions/Then"
+              },
+              "type": "array"
+            }
+          ]
+        },
+        "type": {
+          "enum": [
+            "style",
+            "validation"
+          ],
+          "type": "string"
+        }
+      },
+      "required": [
+        "given",
+        "then"
+      ],
+      "type": "object"
+    },
+    {
+      "$ref": "#/definitions/HumanReadableSeverity"
+    },
+    {
+      "type": "boolean"
+    },
+    {
+      "type": "array",
+      "additionalItems": false,
+      "minItems": 1,
+      "items": [
+        {
+          "$ref": "#/definitions/Severity"
+        },
+        {
+          "type": "object"
         }
-    ]
+      ]
+    }
+  ]
 }
diff --git a/src/meta/ruleset.schema.json b/src/meta/ruleset.schema.json
index 55a80628..a4ec140d 100644
--- a/src/meta/ruleset.schema.json
+++ b/src/meta/ruleset.schema.json
@@ -6,7 +6,7 @@
         "rules": {
             "type": "object",
             "additionalProperties": {
-                "$ref": "rule.schema.json"
+                "$ref": "rule.schema.json#"
             }
         },
         "formats": {
diff --git a/src/rule.ts b/src/rule.ts
new file mode 100644
index 00000000..29b35436
--- /dev/null
+++ b/src/rule.ts
@@ -0,0 +1,42 @@
+import { Optional } from '@stoplight/types';
+
+import { IDocument } from './document';
+import { DEFAULT_SEVERITY_LEVEL, getDiagnosticSeverity } from './rulesets/severity';
+import { IRule, IThen, SpectralDiagnosticSeverity } from './types';
+import { hasIntersectingElement } from './utils';
+
+export class Rule {
+  public readonly name: string;
+  public readonly description: string | null;
+  public readonly message: string | null;
+  public readonly severity: SpectralDiagnosticSeverity;
+  public readonly resolved: boolean;
+  public readonly formats: Optional<string[]>;
+
+  public readonly then: IThen[];
+  public readonly given: string[];
+
+  public get enabled() {
+    return this.severity !== -1;
+  }
+
+  constructor(name: string, rule: IRule) {
+    this.name = name;
+    this.description = rule.description ?? null;
+    this.message = rule.message ?? null;
+    this.severity = rule.severity === void 0 ? DEFAULT_SEVERITY_LEVEL : getDiagnosticSeverity(rule.severity);
+    this.resolved = rule.resolved !== false;
+    this.formats = rule.formats;
+
+    this.then = Array.isArray(rule.then) ? rule.then : [rule.then];
+    this.given = Array.isArray(rule.given) ? rule.given : [rule.given];
+  }
+
+  public matchesFormat(formats: IDocument['formats']) {
+    if (this.formats === void 0) {
+      return true;
+    }
+
+    return Array.isArray(formats) && hasIntersectingElement(this.formats, formats);
+  }
+}
diff --git a/src/rulesets/asyncapi/functions/asyncApi2PayloadValidation.ts b/src/rulesets/asyncapi/functions/asyncApi2PayloadValidation.ts
index 04885653..2d94ee71 100644
--- a/src/rulesets/asyncapi/functions/asyncApi2PayloadValidation.ts
+++ b/src/rulesets/asyncapi/functions/asyncApi2PayloadValidation.ts
@@ -28,7 +28,7 @@ const buildAsyncApi2SchemaObjectValidator = (schemaFn: ISchemaFunction): Validat
   return validator;
 };
 
-export const asyncApi2PayloadValidation: IFunction = function(
+export const asyncApi2PayloadValidation: IFunction<null> = function(
   this: IFunctionContext,
   targetVal,
   _opts,
diff --git a/src/rulesets/message.ts b/src/rulesets/message.ts
index 969c1f5f..96ef2a8e 100644
--- a/src/rulesets/message.ts
+++ b/src/rulesets/message.ts
@@ -5,7 +5,7 @@ import { Replacer } from '../utils/replacer';
 export interface IMessageVars {
   property: Segment;
   error: string;
-  description?: string;
+  description: string | null;
   value: unknown;
   path: string;
 }
diff --git a/src/rulesets/oas/functions/oasPathParam.ts b/src/rulesets/oas/functions/oasPathParam.ts
index adbd400c..fd3ebf91 100644
--- a/src/rulesets/oas/functions/oasPathParam.ts
+++ b/src/rulesets/oas/functions/oasPathParam.ts
@@ -115,6 +115,7 @@ export const oasPathParam: IFunction = (targetVal, _options, paths, vals) => {
         generateResult(`The paths \`${uniquePaths[normalized]}\` and \`${path}\` are equivalent.`, [
           ...paths.given,
           'paths',
+          path,
         ]),
       );
     } else {
diff --git a/src/rulesets/validation.ts b/src/rulesets/validation.ts
index 9c3bdb02..f2ee5286 100644
--- a/src/rulesets/validation.ts
+++ b/src/rulesets/validation.ts
@@ -42,7 +42,7 @@ export function isValidRule(rule: FileRule): rule is IRule {
   return typeof rule === 'object' && rule !== null && !Array.isArray(rule) && ('given' in rule || 'then' in rule);
 }
 
-export function decorateIFunctionWithSchemaValidation(fn: IFunction, schema: JSONSchema) {
+export function decorateIFunctionWithSchemaValidation(fn: IFunction<any>, schema: JSONSchema) {
   return (data: unknown, opts: unknown, ...args: [IFunctionPaths, IFunctionValues]) => {
     if (!ajv.validate(schema, opts)) {
       throw new ValidationError(ajv.errors);
diff --git a/src/runner.ts b/src/runner.ts
deleted file mode 100644
index 23b5747b..00000000
--- a/src/runner.ts
+++ /dev/null
@@ -1,116 +0,0 @@
-const allSettled = require('promise.allsettled');
-const { JSONPath } = require('jsonpath-plus');
-import { DiagnosticSeverity, Optional } from '@stoplight/types';
-import { flatMap } from 'lodash';
-import { STDIN } from './document';
-import { DocumentInventory } from './documentInventory';
-import { lintNode } from './linter';
-import { getDiagnosticSeverity } from './rulesets/severity';
-import { FunctionCollection, IRule, IRuleResult, IRunRule, RunRuleCollection } from './types';
-import { RulesetExceptionCollection } from './types/ruleset';
-import { hasIntersectingElement } from './utils/';
-import { generateDocumentWideResult } from './utils/generateDocumentWideResult';
-import { IExceptionLocation, pivotExceptions } from './utils/pivotExceptions';
-
-export const isRuleEnabled = (rule: IRule) => rule.severity !== void 0 && getDiagnosticSeverity(rule.severity) !== -1;
-
-const isStdInSource = (inventory: DocumentInventory): boolean => {
-  return inventory.document.source === STDIN;
-};
-
-export interface IRunningContext {
-  documentInventory: DocumentInventory;
-  rules: RunRuleCollection;
-  functions: FunctionCollection;
-  exceptions: RulesetExceptionCollection;
-}
-
-const generateDefinedExceptionsButStdIn = (documentInventory: DocumentInventory): IRuleResult => {
-  return generateDocumentWideResult(
-    documentInventory.document,
-    'The ruleset contains `except` entries. However, they cannot be enforced when the input is passed through stdin.',
-    DiagnosticSeverity.Warning,
-    'except-but-stdin',
-  );
-};
-
-export const runRules = async (context: IRunningContext): Promise<IRuleResult[]> => {
-  const { documentInventory, rules, exceptions } = context;
-
-  const results: Array<IRuleResult | Promise<IRuleResult[]>> = [];
-  const isStdIn = isStdInSource(documentInventory);
-  const exceptRuleByLocations = isStdIn ? {} : pivotExceptions(exceptions, rules);
-
-  if (isStdIn && Object.keys(exceptions).length > 0) {
-    results.push(generateDefinedExceptionsButStdIn(documentInventory));
-  }
-
-  for (const rule of Object.values(rules)) {
-    if (!isRuleEnabled(rule)) continue;
-
-    if (
-      rule.formats !== void 0 &&
-      (documentInventory.formats === null ||
-        (documentInventory.formats !== void 0 && !hasIntersectingElement(rule.formats, documentInventory.formats)))
-    ) {
-      continue;
-    }
-
-    runRule(context, rule, exceptRuleByLocations[rule.name], results);
-  }
-
-  return flatMap<PromiseSettledResult<IRuleResult[]>, IRuleResult>(await allSettled(results), result => {
-    if (result.status === 'fulfilled') {
-      return result.value;
-    } else {
-      // todo: use @stoplight/reporter
-      console.warn(result.reason);
-      return [];
-    }
-  });
-};
-
-const runRule = (
-  context: IRunningContext,
-  rule: IRunRule,
-  exceptRuleByLocations: Optional<IExceptionLocation[]>,
-  results: Array<IRuleResult | Promise<IRuleResult[]>>,
-): void => {
-  const target = rule.resolved === false ? context.documentInventory.unresolved : context.documentInventory.resolved;
-
-  for (const given of Array.isArray(rule.given) ? rule.given : [rule.given]) {
-    // don't have to spend time running jsonpath if given is $ - can just use the root object
-    if (given === '$') {
-      results.push(
-        lintNode(
-          context,
-          {
-            path: ['$'],
-            value: target,
-          },
-          rule,
-          exceptRuleByLocations,
-        ),
-      );
-    } else {
-      JSONPath({
-        path: given,
-        json: target,
-        resultType: 'all',
-        callback: (result: any) => {
-          results.push(
-            lintNode(
-              context,
-              {
-                path: JSONPath.toPathArray(result.path),
-                value: result.value,
-              },
-              rule,
-              exceptRuleByLocations,
-            ),
-          );
-        },
-      });
-    }
-  }
-};
diff --git a/src/runner/lintNode.ts b/src/runner/lintNode.ts
new file mode 100644
index 00000000..711b2793
--- /dev/null
+++ b/src/runner/lintNode.ts
@@ -0,0 +1,139 @@
+import { JsonPath, Optional } from '@stoplight/types';
+import { get } from 'lodash';
+
+import { Document } from '../document';
+import { Rule } from '../rule';
+import { IMessageVars, message } from '../rulesets/message';
+import { getDiagnosticSeverity } from '../rulesets/severity';
+import { IFunctionResult, IGivenNode } from '../types';
+import { decodeSegmentFragment, getClosestJsonPath, printPath, PrintStyle } from '../utils';
+import { IRunnerInternalContext } from './types';
+import { getLintTargets, IExceptionLocation, isAKnownException } from './utils';
+
+export const lintNode = (
+  context: IRunnerInternalContext,
+  node: IGivenNode,
+  rule: Rule,
+  exceptionLocations: Optional<IExceptionLocation[]>,
+): void => {
+  const fnContext = {
+    original: node.value,
+    given: node.value,
+    documentInventory: context.documentInventory,
+  };
+
+  const givenPath = node.path.length > 0 && node.path[0] === '$' ? node.path.slice(1) : node.path;
+
+  for (const then of rule.then) {
+    const func = context.functions[then.function];
+    if (typeof func !== 'function') {
+      console.warn(`Function ${then.function} not found. Called by rule ${rule.name}.`);
+      continue;
+    }
+
+    const targets = getLintTargets(node.value, then.field);
+
+    for (const target of targets) {
+      const targetPath = target.path.length > 0 ? [...givenPath, ...target.path] : givenPath;
+
+      let targetResults;
+      try {
+        targetResults = func(
+          target.value,
+          then.functionOptions ?? null,
+          {
+            given: givenPath,
+            target: targetPath,
+          },
+          fnContext,
+        );
+      } catch (ex) {
+        // todo: use reporter or sth
+        console.warn(ex);
+      }
+
+      if (targetResults === void 0) continue;
+
+      if ('then' in targetResults) {
+        context.promises.push(
+          targetResults
+            .then(results =>
+              results === void 0
+                ? void 0
+                : void processTargetResults(
+                    context,
+                    results,
+                    rule,
+                    exceptionLocations,
+                    targetPath, // todo: get rid of it somehow.
+                  ),
+            )
+            .catch(ex => {
+              // todo: use reporter or sth
+              console.warn(ex.message);
+            }),
+        );
+      } else {
+        processTargetResults(
+          context,
+          targetResults,
+          rule,
+          exceptionLocations,
+          targetPath, // todo: get rid of it somehow.
+        );
+      }
+    }
+  }
+};
+
+function processTargetResults(
+  context: IRunnerInternalContext,
+  results: IFunctionResult[],
+  rule: Rule,
+  exceptionLocations: Optional<IExceptionLocation[]>,
+  targetPath: JsonPath,
+): void {
+  for (const result of results) {
+    const escapedJsonPath = (result.path || targetPath).map(decodeSegmentFragment);
+    const associatedItem = context.documentInventory.findAssociatedItemForPath(
+      escapedJsonPath,
+      rule.resolved !== false,
+    );
+    const path = associatedItem?.path || getClosestJsonPath(context.documentInventory.resolved, escapedJsonPath);
+    const source = associatedItem?.document.source;
+
+    if (exceptionLocations !== void 0 && isAKnownException(path, source, exceptionLocations)) {
+      continue;
+    }
+
+    const document = associatedItem?.document || context.documentInventory.document;
+    const range = document.getRangeForJsonPath(path, true) || Document.DEFAULT_RANGE;
+    const value = path.length === 0 ? document.data : get(document.data, path);
+
+    const vars: IMessageVars = {
+      property:
+        associatedItem?.missingPropertyPath && associatedItem.missingPropertyPath.length > path.length
+          ? printPath(associatedItem.missingPropertyPath.slice(path.length - 1), PrintStyle.Dot)
+          : path.length > 0
+          ? path[path.length - 1]
+          : '',
+      error: result.message,
+      path: printPath(path, PrintStyle.EscapedPointer),
+      description: rule.description,
+      value,
+    };
+
+    const resultMessage = message(result.message, vars);
+    vars.error = resultMessage;
+
+    context.results.push({
+      code: rule.name,
+      // todo: rule.isInterpolable
+      message: (rule.message === null ? rule.description ?? resultMessage : message(rule.message, vars)).trim(),
+      path,
+      severity: getDiagnosticSeverity(rule.severity),
+      ...(source !== null && { source }),
+      range,
+    });
+  }
+}
diff --git a/src/runner/runner.ts b/src/runner/runner.ts
new file mode 100644
index 00000000..d7704b71
--- /dev/null
+++ b/src/runner/runner.ts
@@ -0,0 +1,96 @@
+import { DiagnosticSeverity, Optional } from '@stoplight/types';
+import { JSONPathCallback } from 'jsonpath-plus';
+import { STDIN } from '../document';
+import { DocumentInventory } from '../documentInventory';
+import { Rule } from '../rule';
+import { IRuleResult } from '../types';
+import { generateDocumentWideResult } from '../utils/generateDocumentWideResult';
+import { lintNode } from './lintNode';
+import { IRunnerInternalContext, IRunnerPublicContext } from './types';
+import { IExceptionLocation, pivotExceptions } from './utils/pivotExceptions';
+
+const { JSONPath } = require('jsonpath-plus');
+
+const isStdInSource = (inventory: DocumentInventory): boolean => {
+  return inventory.document.source === STDIN;
+};
+
+const generateDefinedExceptionsButStdIn = (documentInventory: DocumentInventory): IRuleResult => {
+  return generateDocumentWideResult(
+    documentInventory.document,
+    'The ruleset contains `except` entries. However, they cannot be enforced when the input is passed through stdin.',
+    DiagnosticSeverity.Warning,
+    'except-but-stdin',
+  );
+};
+
+export const runRules = async (context: IRunnerPublicContext): Promise<IRuleResult[]> => {
+  const { documentInventory, rules, exceptions } = context;
+
+  const runnerContext: IRunnerInternalContext = {
+    ...context,
+    results: [],
+    promises: [],
+  };
+
+  const isStdIn = isStdInSource(documentInventory);
+  const exceptRuleByLocations = isStdIn ? {} : pivotExceptions(exceptions, rules);
+
+  if (isStdIn && Object.keys(exceptions).length > 0) {
+    runnerContext.results.push(generateDefinedExceptionsButStdIn(documentInventory));
+  }
+
+  const relevantRules = Object.values(rules).filter(
+    rule => rule.enabled && rule.matchesFormat(documentInventory.formats),
+  );
+
+  for (const rule of relevantRules) {
+    runRule(runnerContext, rule, exceptRuleByLocations[rule.name]);
+  }
+
+  if (runnerContext.promises.length > 0) {
+    await Promise.all(runnerContext.promises);
+  }
+
+  return runnerContext.results;
+};
+
+const runRule = (
+  context: IRunnerInternalContext,
+  rule: Rule,
+  exceptRuleByLocations: Optional<IExceptionLocation[]>,
+): void => {
+  const target = rule.resolved ? context.documentInventory.resolved : context.documentInventory.unresolved;
+
+  for (const given of rule.given) {
+    // don't have to spend time running jsonpath if given is $ - can just use the root object
+    if (given === '$') {
+      lintNode(
+        context,
+        {
+          path: ['$'],
+          value: target,
+        },
+        rule,
+        exceptRuleByLocations,
+      );
+    } else {
+      JSONPath({
+        path: given,
+        json: target,
+        resultType: 'all',
+        callback: (result => {
+          lintNode(
+            context,
+            {
+              path: JSONPath.toPathArray(result.path),
+              value: result.value,
+            },
+            rule,
+            exceptRuleByLocations,
+          );
+        }) as JSONPathCallback,
+      });
+    }
+  }
+};
diff --git a/src/runner/types.ts b/src/runner/types.ts
new file mode 100644
index 00000000..b564e587
--- /dev/null
+++ b/src/runner/types.ts
@@ -0,0 +1,15 @@
+import { DocumentInventory } from '../documentInventory';
+import { FunctionCollection, IRuleResult, RunRuleCollection } from '../types';
+import { RulesetExceptionCollection } from '../types/ruleset';
+
+export interface IRunnerPublicContext {
+  documentInventory: DocumentInventory;
+  rules: RunRuleCollection;
+  functions: FunctionCollection;
+  exceptions: RulesetExceptionCollection;
+}
+
+export interface IRunnerInternalContext extends IRunnerPublicContext {
+  results: IRuleResult[];
+  promises: Array<Promise<void>>;
+}
diff --git a/src/runner/utils/arePathsEqual.ts b/src/runner/utils/arePathsEqual.ts
new file mode 100644
index 00000000..d141d893
--- /dev/null
+++ b/src/runner/utils/arePathsEqual.ts
@@ -0,0 +1,15 @@
+import { JsonPath } from '@stoplight/types';
+
+export const arePathsEqual = (one: JsonPath, another: JsonPath): boolean => {
+  if (one.length !== another.length) {
+    return false;
+  }
+
+  for (let i = 0; i < one.length; i++) {
+    if (one[i] !== another[i]) {
+      return false;
+    }
+  }
+
+  return true;
+};
diff --git a/src/utils/getLintTargets.ts b/src/runner/utils/getLintTargets.ts
similarity index 100%
rename from src/utils/getLintTargets.ts
rename to src/runner/utils/getLintTargets.ts
diff --git a/src/runner/utils/index.ts b/src/runner/utils/index.ts
new file mode 100644
index 00000000..c2b28a37
--- /dev/null
+++ b/src/runner/utils/index.ts
@@ -0,0 +1,4 @@
+export * from './arePathsEqual';
+export * from './getLintTargets';
+export * from './isAKnownException';
+export * from './pivotExceptions';
diff --git a/src/runner/utils/isAKnownException.ts b/src/runner/utils/isAKnownException.ts
new file mode 100644
index 00000000..caa71a89
--- /dev/null
+++ b/src/runner/utils/isAKnownException.ts
@@ -0,0 +1,21 @@
+import { JsonPath, Optional } from '@stoplight/types';
+import { arePathsEqual } from './arePathsEqual';
+import { IExceptionLocation } from './pivotExceptions';
+
+export const isAKnownException = (
+  path: JsonPath,
+  source: Optional<string | null>,
+  locations: IExceptionLocation[],
+): boolean => {
+  for (const location of locations) {
+    if (source !== location.source) {
+      continue;
+    }
+
+    if (arePathsEqual(path, location.path)) {
+      return true;
+    }
+  }
+
+  return false;
+};
diff --git a/src/utils/pivotExceptions.ts b/src/runner/utils/pivotExceptions.ts
similarity index 82%
rename from src/utils/pivotExceptions.ts
rename to src/runner/utils/pivotExceptions.ts
index ade1b395..df549041 100644
--- a/src/utils/pivotExceptions.ts
+++ b/src/runner/utils/pivotExceptions.ts
@@ -1,8 +1,8 @@
 import { extractPointerFromRef, extractSourceFromRef, pointerToPath } from '@stoplight/json';
 import { Dictionary, JsonPath } from '@stoplight/types';
-import { InvalidUriError } from '../rulesets/mergers/exceptions';
-import { IRunRule } from '../types';
-import { RulesetExceptionCollection } from '../types/ruleset';
+import { InvalidUriError } from '../../rulesets/mergers/exceptions';
+import { RunRuleCollection } from '../../types';
+import { RulesetExceptionCollection } from '../../types/ruleset';
 
 export interface IExceptionLocation {
   source: string;
@@ -11,7 +11,7 @@ export interface IExceptionLocation {
 
 export const pivotExceptions = (
   exceptions: RulesetExceptionCollection,
-  runRules: Dictionary<IRunRule, string>,
+  runRules: RunRuleCollection,
 ): Dictionary<IExceptionLocation[], string> => {
   const dic: Dictionary<IExceptionLocation[], string> = {};
 
diff --git a/src/spectral.ts b/src/spectral.ts
index 2b88084b..d98f2f74 100644
--- a/src/spectral.ts
+++ b/src/spectral.ts
@@ -10,12 +10,12 @@ import { DocumentInventory } from './documentInventory';
 import { CoreFunctions, functions as coreFunctions } from './functions';
 import * as Parsers from './parsers';
 import request from './request';
+import { Rule } from './rule';
 import { readRuleset } from './rulesets';
 import { compileExportedFunction, setFunctionContext } from './rulesets/evaluators';
 import { mergeExceptions } from './rulesets/mergers/exceptions';
 import { IRulesetReadOptions } from './rulesets/reader';
-import { DEFAULT_SEVERITY_LEVEL, getDiagnosticSeverity } from './rulesets/severity';
-import { runRules } from './runner';
+import { runRules } from './runner/runner';
 import {
   FormatLookup,
   FunctionCollection,
@@ -126,25 +126,14 @@ export class Spectral {
   public setRules(rules: RuleCollection) {
     empty(this.rules);
 
-    for (const name in rules) {
-      if (!rules.hasOwnProperty(name)) continue;
-      const rule = rules[name];
-
-      this.rules[name] = {
-        name,
-        ...rule,
-        severity: rule.severity === void 0 ? DEFAULT_SEVERITY_LEVEL : getDiagnosticSeverity(rule.severity),
-      };
+    for (const [name, rule] of Object.entries(rules)) {
+      this.rules[name] = new Rule(name, rule);
     }
   }
 
   public mergeRules(rules: PartialRuleCollection) {
-    for (const name in rules) {
-      if (!rules.hasOwnProperty(name)) continue;
-      const rule = rules[name];
-      if (rule) {
-        this.rules[name] = merge(this.rules[name], rule);
-      }
+    for (const [name, rule] of Object.entries(rules)) {
+      this.rules[name] = merge(this.rules[name], rule);
     }
   }
 
diff --git a/src/types/function.ts b/src/types/function.ts
index a3689aef..b0ae93e0 100644
--- a/src/types/function.ts
+++ b/src/types/function.ts
@@ -7,9 +7,9 @@ export interface IFunctionContext {
   cache: Map<unknown, unknown>;
 }
 
-export type IFunction<O = any> = (
+export type IFunction<O extends object | null = null> = (
   targetValue: any,
-  options: O,
+  options: O extends null ? null : O,
   paths: IFunctionPaths,
   otherValues: IFunctionValues,
 ) => void | IFunctionResult[] | Promise<void | IFunctionResult[]>;
diff --git a/src/types/spectral.ts b/src/types/spectral.ts
index f5478327..a764a0cb 100644
--- a/src/types/spectral.ts
+++ b/src/types/spectral.ts
@@ -2,17 +2,13 @@ import { IResolveOpts, IResolveResult } from '@stoplight/json-ref-resolver/types
 import { DiagnosticSeverity, Dictionary, IDiagnostic, JsonPath } from '@stoplight/types';
 import { JSONSchema4, JSONSchema6, JSONSchema7 } from 'json-schema';
 import { IFunction, IRule } from '.';
+import { Rule } from '../rule';
 import { ComputeFingerprintFunc } from '../utils';
 
-export type FunctionCollection = Dictionary<IFunction, string>;
+export type FunctionCollection = Dictionary<IFunction<any>, string>;
 export type RuleCollection = Dictionary<IRule, string>;
 export type PartialRuleCollection = Dictionary<Partial<IRule>, string>;
-export type RunRuleCollection = Dictionary<IRunRule, string>;
-
-export interface IRunRule extends IRule {
-  name: string;
-  severity: SpectralDiagnosticSeverity;
-}
+export type RunRuleCollection = Dictionary<Rule, string>;
 
 export type SpectralDiagnosticSeverity = DiagnosticSeverity | -1;
 
diff --git a/src/utils/decodeSegmentFragment.ts b/src/utils/decodeSegmentFragment.ts
new file mode 100644
index 00000000..83ea4956
--- /dev/null
+++ b/src/utils/decodeSegmentFragment.ts
@@ -0,0 +1,6 @@
+import { decodePointerFragment } from '@stoplight/json';
+import { Segment } from '@stoplight/types';
+
+export function decodeSegmentFragment(segment: Segment): string {
+  return typeof segment !== 'string' ? String(segment) : decodePointerFragment(segment);
+}
diff --git a/src/utils/index.ts b/src/utils/index.ts
index be867a2b..e19c3913 100644
--- a/src/utils/index.ts
+++ b/src/utils/index.ts
@@ -1,5 +1,5 @@
 export * from './empty';
-export * from './getLintTargets';
+export * from './decodeSegmentFragment';
 export * from './hasIntersectingElement';
 export * from './refs';
 export * from './prepareResults';
diff --git a/yarn.lock b/yarn.lock
index 052f51d4..4e0709e9 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -68,6 +68,11 @@
   dependencies:
     "@babel/types" "^7.8.3"
 
+"@babel/helper-validator-identifier@^7.9.5":
+  version "7.9.5"
+  resolved "https://registry.yarnpkg.com/@babel/helper-validator-identifier/-/helper-validator-identifier-7.9.5.tgz#90977a8e6fbf6b431a7dc31752eee233bf052d80"
+  integrity sha512-/8arLKUFq882w4tWGj9JYzRpAlZgiWUJ+dtteNTDqrRBz9Iguck9Rn3ykuBDoUwh2TO4tSAJlrxDUOXWklJe4g==
+
 "@babel/helpers@^7.8.3":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/helpers/-/helpers-7.8.3.tgz#382fbb0382ce7c4ce905945ab9641d688336ce85"
@@ -91,6 +96,11 @@
   resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.4.tgz#68a35e6b0319bbc014465be43828300113f2f2e8"
   integrity sha512-bC49otXX6N0/VYhgOMh4gnP26E9xnDZK3TmbNpxYzzz9BQLBosQwfyOe9/cXUU3txYhTzLCbcqd5c8y/OmCjHA==
 
+"@babel/parser@^7.8.6":
+  version "7.9.6"
+  resolved "https://registry.yarnpkg.com/@babel/parser/-/parser-7.9.6.tgz#3b1bbb30dabe600cd72db58720998376ff653bc7"
+  integrity sha512-AoeIEJn8vt+d/6+PXDRPaksYhnlbMIiejioBZvvMQsOjW/JYK6k/0dKnvvP3EhK5GfMBWDPtrxRtegWdAcdq9Q==
+
 "@babel/plugin-syntax-async-generators@^7.8.4":
   version "7.8.4"
   resolved "https://registry.yarnpkg.com/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz#a983fb1aeb2ec3f6ed042a210f640e90e786fe0d"
@@ -168,6 +178,15 @@
   dependencies:
     regenerator-runtime "^0.13.4"
 
+"@babel/template@^7.3.3":
+  version "7.8.6"
+  resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.6.tgz#86b22af15f828dfb086474f964dcc3e39c43ce2b"
+  integrity sha512-zbMsPMy/v0PWFZEhQJ66bqjhH+z0JgMoBWuikXybgG3Gkd/3t5oQ1Rw2WQhnSrsOmsKXnZOx15tkC4qON/+JPg==
+  dependencies:
+    "@babel/code-frame" "^7.8.3"
+    "@babel/parser" "^7.8.6"
+    "@babel/types" "^7.8.6"
+
 "@babel/template@^7.7.4", "@babel/template@^7.8.3":
   version "7.8.3"
   resolved "https://registry.yarnpkg.com/@babel/template/-/template-7.8.3.tgz#e02ad04fe262a657809327f578056ca15fd4d1b8"
@@ -201,6 +220,15 @@
     lodash "^4.17.13"
     to-fast-properties "^2.0.0"
 
+"@babel/types@^7.3.3", "@babel/types@^7.8.6":
+  version "7.9.6"
+  resolved "https://registry.yarnpkg.com/@babel/types/-/types-7.9.6.tgz#2c5502b427251e9de1bd2dff95add646d95cc9f7"
+  integrity sha512-qxXzvBO//jO9ZnoasKF1uJzHd2+M6Q2ZPIVfnFps8JJvXy0ZBbwbNOmE6SGIY5XOY6d1Bo5lb9d9RJ8nv3WSeA==
+  dependencies:
+    "@babel/helper-validator-identifier" "^7.9.5"
+    lodash "^4.17.13"
+    to-fast-properties "^2.0.0"
+
 "@bcoe/v8-coverage@^0.2.3":
   version "0.2.3"
   resolved "https://registry.yarnpkg.com/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz#75a2e8b51cb758a7553d6804a5932d7aace75c39"
@@ -245,44 +273,44 @@
     chalk "^2.0.1"
     slash "^2.0.0"
 
-"@jest/console@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.4.0.tgz#e2760b532701137801ba824dcff6bc822c961bac"
-  integrity sha512-CfE0erx4hdJ6t7RzAcE1wLG6ZzsHSmybvIBQDoCkDM1QaSeWL9wJMzID/2BbHHa7ll9SsbbK43HjbERbBaFX2A==
+"@jest/console@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/console/-/console-25.5.0.tgz#770800799d510f37329c508a9edd0b7b447d9abb"
+  integrity sha512-T48kZa6MK1Y6k4b89sexwmSF4YLeZS/Udqg3Jj3jG/cHH+N/sLFCEoXEDMOKugJQ9FxPN1osxIknvKkxt6MKyw==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
-    jest-message-util "^25.4.0"
-    jest-util "^25.4.0"
+    jest-message-util "^25.5.0"
+    jest-util "^25.5.0"
     slash "^3.0.0"
 
-"@jest/core@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.4.0.tgz#cc1fe078df69b8f0fbb023bb0bcee23ef3b89411"
-  integrity sha512-h1x9WSVV0+TKVtATGjyQIMJENs8aF6eUjnCoi4jyRemYZmekLr8EJOGQqTWEX8W6SbZ6Skesy9pGXrKeAolUJw==
+"@jest/core@^25.5.4":
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/@jest/core/-/core-25.5.4.tgz#3ef7412f7339210f003cdf36646bbca786efe7b4"
+  integrity sha512-3uSo7laYxF00Dg/DMgbn4xMJKmDdWvZnf89n8Xj/5/AeQ2dOQmn6b6Hkj/MleyzZWXpwv+WSdYWl4cLsy2JsoA==
   dependencies:
-    "@jest/console" "^25.4.0"
-    "@jest/reporters" "^25.4.0"
-    "@jest/test-result" "^25.4.0"
-    "@jest/transform" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/console" "^25.5.0"
+    "@jest/reporters" "^25.5.1"
+    "@jest/test-result" "^25.5.0"
+    "@jest/transform" "^25.5.1"
+    "@jest/types" "^25.5.0"
     ansi-escapes "^4.2.1"
     chalk "^3.0.0"
     exit "^0.1.2"
-    graceful-fs "^4.2.3"
-    jest-changed-files "^25.4.0"
-    jest-config "^25.4.0"
-    jest-haste-map "^25.4.0"
-    jest-message-util "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-changed-files "^25.5.0"
+    jest-config "^25.5.4"
+    jest-haste-map "^25.5.1"
+    jest-message-util "^25.5.0"
     jest-regex-util "^25.2.6"
-    jest-resolve "^25.4.0"
-    jest-resolve-dependencies "^25.4.0"
-    jest-runner "^25.4.0"
-    jest-runtime "^25.4.0"
-    jest-snapshot "^25.4.0"
-    jest-util "^25.4.0"
-    jest-validate "^25.4.0"
-    jest-watcher "^25.4.0"
+    jest-resolve "^25.5.1"
+    jest-resolve-dependencies "^25.5.4"
+    jest-runner "^25.5.4"
+    jest-runtime "^25.5.4"
+    jest-snapshot "^25.5.1"
+    jest-util "^25.5.0"
+    jest-validate "^25.5.0"
+    jest-watcher "^25.5.0"
     micromatch "^4.0.2"
     p-each-series "^2.1.0"
     realpath-native "^2.0.0"
@@ -290,49 +318,59 @@
     slash "^3.0.0"
     strip-ansi "^6.0.0"
 
-"@jest/environment@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.4.0.tgz#45071f525f0d8c5a51ed2b04fd42b55a8f0c7cb3"
-  integrity sha512-KDctiak4mu7b4J6BIoN/+LUL3pscBzoUCP+EtSPd2tK9fqyDY5OF+CmkBywkFWezS9tyH5ACOQNtpjtueEDH6Q==
+"@jest/environment@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/environment/-/environment-25.5.0.tgz#aa33b0c21a716c65686638e7ef816c0e3a0c7b37"
+  integrity sha512-U2VXPEqL07E/V7pSZMSQCvV5Ea4lqOlT+0ZFijl/i316cRMHvZ4qC+jBdryd+lmRetjQo0YIQr6cVPNxxK87mA==
   dependencies:
-    "@jest/fake-timers" "^25.4.0"
-    "@jest/types" "^25.4.0"
-    jest-mock "^25.4.0"
+    "@jest/fake-timers" "^25.5.0"
+    "@jest/types" "^25.5.0"
+    jest-mock "^25.5.0"
 
-"@jest/fake-timers@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.4.0.tgz#3a9a4289ba836abd084953dca406389a57e00fbd"
-  integrity sha512-lI9z+VOmVX4dPPFzyj0vm+UtaB8dCJJ852lcDnY0uCPRvZAaVGnMwBBc1wxtf+h7Vz6KszoOvKAt4QijDnHDkg==
+"@jest/fake-timers@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/fake-timers/-/fake-timers-25.5.0.tgz#46352e00533c024c90c2bc2ad9f2959f7f114185"
+  integrity sha512-9y2+uGnESw/oyOI3eww9yaxdZyHq7XvprfP/eeoCsjqKYts2yRlsHS/SgjPDV8FyMfn2nbMy8YzUk6nyvdLOpQ==
   dependencies:
-    "@jest/types" "^25.4.0"
-    jest-message-util "^25.4.0"
-    jest-mock "^25.4.0"
-    jest-util "^25.4.0"
+    "@jest/types" "^25.5.0"
+    jest-message-util "^25.5.0"
+    jest-mock "^25.5.0"
+    jest-util "^25.5.0"
     lolex "^5.0.0"
 
-"@jest/reporters@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.4.0.tgz#836093433b32ce4e866298af2d6fcf6ed351b0b0"
-  integrity sha512-bhx/buYbZgLZm4JWLcRJ/q9Gvmd3oUh7k2V7gA4ZYBx6J28pIuykIouclRdiAC6eGVX1uRZT+GK4CQJLd/PwPg==
+"@jest/globals@^25.5.2":
+  version "25.5.2"
+  resolved "https://registry.yarnpkg.com/@jest/globals/-/globals-25.5.2.tgz#5e45e9de8d228716af3257eeb3991cc2e162ca88"
+  integrity sha512-AgAS/Ny7Q2RCIj5kZ+0MuKM1wbF0WMLxbCVl/GOMoCNbODRdJ541IxJ98xnZdVSZXivKpJlNPIWa3QmY0l4CXA==
+  dependencies:
+    "@jest/environment" "^25.5.0"
+    "@jest/types" "^25.5.0"
+    expect "^25.5.0"
+
+"@jest/reporters@^25.5.1":
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/@jest/reporters/-/reporters-25.5.1.tgz#cb686bcc680f664c2dbaf7ed873e93aa6811538b"
+  integrity sha512-3jbd8pPDTuhYJ7vqiHXbSwTJQNavczPs+f1kRprRDxETeE3u6srJ+f0NPuwvOmk+lmunZzPkYWIFZDLHQPkviw==
   dependencies:
     "@bcoe/v8-coverage" "^0.2.3"
-    "@jest/console" "^25.4.0"
-    "@jest/test-result" "^25.4.0"
-    "@jest/transform" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/console" "^25.5.0"
+    "@jest/test-result" "^25.5.0"
+    "@jest/transform" "^25.5.1"
+    "@jest/types" "^25.5.0"
     chalk "^3.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.0"
     istanbul-lib-report "^3.0.0"
     istanbul-lib-source-maps "^4.0.0"
     istanbul-reports "^3.0.2"
-    jest-haste-map "^25.4.0"
-    jest-resolve "^25.4.0"
-    jest-util "^25.4.0"
-    jest-worker "^25.4.0"
+    jest-haste-map "^25.5.1"
+    jest-resolve "^25.5.1"
+    jest-util "^25.5.0"
+    jest-worker "^25.5.0"
     slash "^3.0.0"
     source-map "^0.6.0"
     string-length "^3.1.0"
@@ -350,13 +388,13 @@
     graceful-fs "^4.1.15"
     source-map "^0.6.0"
 
-"@jest/source-map@^25.2.6":
-  version "25.2.6"
-  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.2.6.tgz#0ef2209514c6d445ebccea1438c55647f22abb4c"
-  integrity sha512-VuIRZF8M2zxYFGTEhkNSvQkUKafQro4y+mwUxy5ewRqs5N/ynSFUODYp3fy1zCnbCMy1pz3k+u57uCqx8QRSQQ==
+"@jest/source-map@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/source-map/-/source-map-25.5.0.tgz#df5c20d6050aa292c2c6d3f0d2c7606af315bd1b"
+  integrity sha512-eIGx0xN12yVpMcPaVpjXPnn3N30QGJCJQSkEDUt9x1fI1Gdvb07Ml6K5iN2hG7NmMP6FDmtPEssE3z6doOYUwQ==
   dependencies:
     callsites "^3.0.0"
-    graceful-fs "^4.2.3"
+    graceful-fs "^4.2.4"
     source-map "^0.6.0"
 
 "@jest/test-result@^24.9.0":
@@ -368,41 +406,42 @@
     "@jest/types" "^24.9.0"
     "@types/istanbul-lib-coverage" "^2.0.0"
 
-"@jest/test-result@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.4.0.tgz#6f2ec2c8da9981ef013ad8651c1c6f0cb20c6324"
-  integrity sha512-8BAKPaMCHlL941eyfqhWbmp3MebtzywlxzV+qtngQ3FH+RBqnoSAhNEPj4MG7d2NVUrMOVfrwuzGpVIK+QnMAA==
+"@jest/test-result@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/test-result/-/test-result-25.5.0.tgz#139a043230cdeffe9ba2d8341b27f2efc77ce87c"
+  integrity sha512-oV+hPJgXN7IQf/fHWkcS99y0smKLU2czLBJ9WA0jHITLst58HpQMtzSYxzaBvYc6U5U6jfoMthqsUlUlbRXs0A==
   dependencies:
-    "@jest/console" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/console" "^25.5.0"
+    "@jest/types" "^25.5.0"
     "@types/istanbul-lib-coverage" "^2.0.0"
     collect-v8-coverage "^1.0.0"
 
-"@jest/test-sequencer@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.4.0.tgz#2b96f9d37f18dc3336b28e3c8070f97f9f55f43b"
-  integrity sha512-240cI+nsM3attx2bMp9uGjjHrwrpvxxrZi8Tyqp/cfOzl98oZXVakXBgxODGyBYAy/UGXPKXLvNc2GaqItrsJg==
+"@jest/test-sequencer@^25.5.4":
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/@jest/test-sequencer/-/test-sequencer-25.5.4.tgz#9b4e685b36954c38d0f052e596d28161bdc8b737"
+  integrity sha512-pTJGEkSeg1EkCO2YWq6hbFvKNXk8ejqlxiOg1jBNLnWrgXOkdY6UmqZpwGFXNnRt9B8nO1uWMzLLZ4eCmhkPNA==
   dependencies:
-    "@jest/test-result" "^25.4.0"
-    jest-haste-map "^25.4.0"
-    jest-runner "^25.4.0"
-    jest-runtime "^25.4.0"
+    "@jest/test-result" "^25.5.0"
+    graceful-fs "^4.2.4"
+    jest-haste-map "^25.5.1"
+    jest-runner "^25.5.4"
+    jest-runtime "^25.5.4"
 
-"@jest/transform@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.4.0.tgz#eef36f0367d639e2fd93dccd758550377fbb9962"
-  integrity sha512-t1w2S6V1sk++1HHsxboWxPEuSpN8pxEvNrZN+Ud/knkROWtf8LeUmz73A4ezE8476a5AM00IZr9a8FO9x1+j3g==
+"@jest/transform@^25.5.1":
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/@jest/transform/-/transform-25.5.1.tgz#0469ddc17699dd2bf985db55fa0fb9309f5c2db3"
+  integrity sha512-Y8CEoVwXb4QwA6Y/9uDkn0Xfz0finGkieuV0xkdF9UtZGJeLukD5nLkaVrVsODB1ojRWlaoD0AJZpVHCSnJEvg==
   dependencies:
     "@babel/core" "^7.1.0"
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     babel-plugin-istanbul "^6.0.0"
     chalk "^3.0.0"
     convert-source-map "^1.4.0"
     fast-json-stable-stringify "^2.0.0"
-    graceful-fs "^4.2.3"
-    jest-haste-map "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-haste-map "^25.5.1"
     jest-regex-util "^25.2.6"
-    jest-util "^25.4.0"
+    jest-util "^25.5.0"
     micromatch "^4.0.2"
     pirates "^4.0.1"
     realpath-native "^2.0.0"
@@ -419,16 +458,26 @@
     "@types/istanbul-reports" "^1.1.1"
     "@types/yargs" "^13.0.0"
 
-"@jest/types@^25.3.0", "@jest/types@^25.4.0":
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.4.0.tgz#5afeb8f7e1cba153a28e5ac3c9fe3eede7206d59"
-  integrity sha512-XBeaWNzw2PPnGW5aXvZt3+VO60M+34RY3XDsCK5tW7kyj3RK0XClRutCfjqcBuaR2aBQTbluEDME9b5MB9UAPw==
+"@jest/types@^25.4.0", "@jest/types@^25.5.0":
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-25.5.0.tgz#4d6a4793f7b9599fc3680877b856a97dbccf2a9d"
+  integrity sha512-OXD0RgQ86Tu3MazKo8bnrkDRaDXXMGUqd+kTtLtK1Zb7CRzQcaSRPPPV37SvYTdevXEBVxe0HXylEjs8ibkmCw==
   dependencies:
     "@types/istanbul-lib-coverage" "^2.0.0"
     "@types/istanbul-reports" "^1.1.1"
     "@types/yargs" "^15.0.0"
     chalk "^3.0.0"
 
+"@jest/types@^26.0.1":
+  version "26.0.1"
+  resolved "https://registry.yarnpkg.com/@jest/types/-/types-26.0.1.tgz#b78333fbd113fa7aec8d39de24f88de8686dac67"
+  integrity sha512-IbtjvqI9+eS1qFnOIEL7ggWmT+iK/U+Vde9cGWtYb/b6XgKb3X44ZAe/z9YZzoAAZ/E92m0DqrilF934IGNnQA==
+  dependencies:
+    "@types/istanbul-lib-coverage" "^2.0.0"
+    "@types/istanbul-reports" "^1.1.1"
+    "@types/yargs" "^15.0.0"
+    chalk "^4.0.0"
+
 "@nodelib/fs.scandir@2.1.3":
   version "2.1.3"
   resolved "https://registry.yarnpkg.com/@nodelib/fs.scandir/-/fs.scandir-2.1.3.tgz#3a582bdb53804c6ba6d146579c46e52130cf4a3b"
@@ -619,6 +668,13 @@
   resolved "https://registry.yarnpkg.com/@types/fetch-mock/-/fetch-mock-7.3.2.tgz#58805ba36a9357be92cc8c008dbfda937e9f7d8f"
   integrity sha512-NCEfv49jmDsBAixjMjEHKVgmVQlJ+uK56FOc+2roYPExnXCZDpi6mJOHQ3v23BiO84hBDStND9R2itJr7PNoow==
 
+"@types/graceful-fs@^4.1.2":
+  version "4.1.3"
+  resolved "https://registry.yarnpkg.com/@types/graceful-fs/-/graceful-fs-4.1.3.tgz#039af35fe26bec35003e8d86d2ee9c586354348f"
+  integrity sha512-AiHRaEB50LQg0pZmm659vNBb9f4SJ0qrAnteuzhSeAUcJKxoYgEnprg/83kppCnc2zvtCKbdZry1a5pVY3lOTQ==
+  dependencies:
+    "@types/node" "*"
+
 "@types/istanbul-lib-coverage@*", "@types/istanbul-lib-coverage@^2.0.0", "@types/istanbul-lib-coverage@^2.0.1":
   version "2.0.1"
   resolved "https://registry.yarnpkg.com/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.1.tgz#42995b446db9a48a11a07ec083499a860e9138ff"
@@ -727,10 +783,10 @@
   resolved "https://registry.yarnpkg.com/@types/text-table/-/text-table-0.2.1.tgz#39c4d4a058a82f677392dfd09976e83d9b4c9264"
   integrity sha512-dchbFCWfVgUSWEvhOkXGS7zjm+K7jCUvGrQkAHPk2Fmslfofp4HQTH2pqnQ3Pw5GPYv0zWa2AQjKtsfZThuemQ==
 
-"@types/tmp@^0.1.0":
-  version "0.1.0"
-  resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.1.0.tgz#19cf73a7bcf641965485119726397a096f0049bd"
-  integrity sha512-6IwZ9HzWbCq6XoQWhxLpDjuADodH/MKXRUIDFudvgjcVdjFknvmR+DNsoUeer4XPrEnrZs04Jj+kfV9pFsrhmA==
+"@types/tmp@^0.2.0":
+  version "0.2.0"
+  resolved "https://registry.yarnpkg.com/@types/tmp/-/tmp-0.2.0.tgz#e3f52b4d7397eaa9193592ef3fdd44dc0af4298c"
+  integrity sha512-flgpHJjntpBAdJD43ShRosQvNC0ME97DCfGvZEDlAThQmnerRXrLbX6YgzRBQCZTthET9eAWFAMaYP0m0Y4HzQ==
 
 "@types/urijs@^1.19":
   version "1.19.4"
@@ -830,6 +886,14 @@ agent-base@~4.2.1:
   dependencies:
     es6-promisify "^5.0.0"
 
+aggregate-error@^3.0.0:
+  version "3.0.1"
+  resolved "https://registry.yarnpkg.com/aggregate-error/-/aggregate-error-3.0.1.tgz#db2fe7246e536f40d9b5442a39e117d7dd6a24e0"
+  integrity sha512-quoaXsZ9/BLNae5yiNoUz+Nhkwz83GhWwtYFglcjEQB2NDHCIpApbqXxIFnm4Pq/Nvhrsq5sYJFyohrrxnTGAA==
+  dependencies:
+    clean-stack "^2.0.0"
+    indent-string "^4.0.0"
+
 ajv-oai@1.2.0:
   version "1.2.0"
   resolved "https://registry.yarnpkg.com/ajv-oai/-/ajv-oai-1.2.0.tgz#93ba0d3c64edf55e575c9d9f52fe494251c5b6d0"
@@ -847,10 +911,10 @@ ajv@6.12.2, ajv@^6.5.5:
     json-schema-traverse "^0.4.1"
     uri-js "^4.2.2"
 
-ansi-escapes@^3.0.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-3.2.0.tgz#8780b98ff9dbf5638152d1f1fe5c1d7b4442976b"
-  integrity sha512-cBhpre4ma+U0T1oM5fXg7Dy1Jw7zzwv7lt/GoCpr+hDQJoYnKVPLL4dCvSEFMmQurOQvSrwT7SL/DAlhBI97RQ==
+ansi-colors@^3.2.1:
+  version "3.2.4"
+  resolved "https://registry.yarnpkg.com/ansi-colors/-/ansi-colors-3.2.4.tgz#e3a3da4bfbae6c86a9c285625de124a234026fbf"
+  integrity sha512-hHUXGagefjN2iRrID63xckIvotOXOojhQKWIPUZ4mNUZ9nLZW+7FMNoE1lOkEhNWYsx/7ysGIuJYCiMAA9FnrA==
 
 ansi-escapes@^4.2.1:
   version "4.3.0"
@@ -859,15 +923,12 @@ ansi-escapes@^4.2.1:
   dependencies:
     type-fest "^0.8.1"
 
-ansi-regex@^2.0.0:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-2.1.1.tgz#c3b33ab5ee360d86e0e628f0468ae7ef27d654df"
-  integrity sha1-w7M6te42DYbg5ijwRorn7yfWVN8=
-
-ansi-regex@^3.0.0:
-  version "3.0.0"
-  resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-3.0.0.tgz#ed0317c322064f79466c02966bddb605ab37d998"
-  integrity sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=
+ansi-escapes@^4.3.0:
+  version "4.3.1"
+  resolved "https://registry.yarnpkg.com/ansi-escapes/-/ansi-escapes-4.3.1.tgz#a5c47cc43181f1f38ffd7076837700d395522a61"
+  integrity sha512-JWF7ocqNrp8u9oqpgV+wH5ftbt+cfvv+PTjOvKLT3AdYly/LmORARfEVT1iyjwN+4MqE5UmVKoAdIBqeoCHgLA==
+  dependencies:
+    type-fest "^0.11.0"
 
 ansi-regex@^4.0.0, ansi-regex@^4.1.0:
   version "4.1.0"
@@ -879,11 +940,6 @@ ansi-regex@^5.0.0:
   resolved "https://registry.yarnpkg.com/ansi-regex/-/ansi-regex-5.0.0.tgz#388539f55179bf39339c81af30a654d69f87cb75"
   integrity sha512-bY6fj56OUQ0hU1KjFNDQuJFezqKdrAyFdIevADiqrWHwSlbmBNMHp5ak2f40Pm8JTFyM2mqxkG6ngkHO11f/lg==
 
-ansi-styles@^2.2.1:
-  version "2.2.1"
-  resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-2.2.1.tgz#b432dd3358b634cf75e1e4664368240533c1ddbe"
-  integrity sha1-tDLdM1i2NM914eRmQ2gkBTPB3b4=
-
 ansi-styles@^3.2.0, ansi-styles@^3.2.1:
   version "3.2.1"
   resolved "https://registry.yarnpkg.com/ansi-styles/-/ansi-styles-3.2.1.tgz#41fbb20243e50b12be0f04b8dedbf07520ce841d"
@@ -1004,16 +1060,6 @@ array-unique@^0.3.2:
   resolved "https://registry.yarnpkg.com/array-unique/-/array-unique-0.3.2.tgz#a894b75d4bc4f6cd679ef3244a9fd8f46ae2d428"
   integrity sha1-qJS3XUvE9s1nnvMkSp/Y9Gri1Cg=
 
-array.prototype.map@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/array.prototype.map/-/array.prototype.map-1.0.2.tgz#9a4159f416458a23e9483078de1106b2ef68f8ec"
-  integrity sha512-Az3OYxgsa1g7xDYp86l0nnN4bcmuEITGe1rbdEBVkrqkzMgDcbdQ2R7r41pNzti+4NMces3H8gMmuioZUilLgw==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
-    es-array-method-boxes-properly "^1.0.0"
-    is-string "^1.0.4"
-
 arraybuffer.slice@~0.0.7:
   version "0.0.7"
   resolved "https://registry.yarnpkg.com/arraybuffer.slice/-/arraybuffer.slice-0.0.7.tgz#3bbc4275dd584cc1b10809b89d4e8b63a69e7675"
@@ -1065,6 +1111,11 @@ astral-regex@^1.0.0:
   resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-1.0.0.tgz#6c8c3fb827dd43ee3918f27b82782ab7658a6fd9"
   integrity sha512-+Ryf6g3BKoRc7jfp7ad8tM4TtMiaWvbF/1/sQcZPkkS7ag3D5nMBCe2UfOTONtAkaG0tO0ij3C5Lwmf1EiyjHg==
 
+astral-regex@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/astral-regex/-/astral-regex-2.0.0.tgz#483143c567aeed4785759c0865786dc77d7d2e31"
+  integrity sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==
+
 async-limiter@~1.0.0:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/async-limiter/-/async-limiter-1.0.1.tgz#dd379e94f0db8310b08291f9d64c3209766617fd"
@@ -1107,17 +1158,18 @@ aws4@^1.8.0:
   resolved "https://registry.yarnpkg.com/aws4/-/aws4-1.8.0.tgz#f0e003d9ca9e7f59c7a508945d7b2ef9a04a542f"
   integrity sha512-ReZxvNHIOv88FlT7rxcXIIC0fPt4KZqZbOlivyWtXLt8ESx84zd3kMC6iK5jVeS2qt+g7ftS7ye4fi06X5rtRQ==
 
-babel-jest@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.4.0.tgz#409eb3e2ddc2ad9a92afdbb00991f1633f8018d0"
-  integrity sha512-p+epx4K0ypmHuCnd8BapfyOwWwosNCYhedetQey1awddtfmEX0MmdxctGl956uwUmjwXR5VSS5xJcGX9DvdIog==
+babel-jest@^25.5.1:
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/babel-jest/-/babel-jest-25.5.1.tgz#bc2e6101f849d6f6aec09720ffc7bc5332e62853"
+  integrity sha512-9dA9+GmMjIzgPnYtkhBg73gOo/RHqPmLruP3BaGL4KEX3Dwz6pI8auSN8G8+iuEG90+GSswyKvslN+JYSaacaQ==
   dependencies:
-    "@jest/transform" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/transform" "^25.5.1"
+    "@jest/types" "^25.5.0"
     "@types/babel__core" "^7.1.7"
     babel-plugin-istanbul "^6.0.0"
-    babel-preset-jest "^25.4.0"
+    babel-preset-jest "^25.5.0"
     chalk "^3.0.0"
+    graceful-fs "^4.2.4"
     slash "^3.0.0"
 
 babel-plugin-istanbul@^6.0.0:
@@ -1131,11 +1183,13 @@ babel-plugin-istanbul@^6.0.0:
     istanbul-lib-instrument "^4.0.0"
     test-exclude "^6.0.0"
 
-babel-plugin-jest-hoist@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.4.0.tgz#0c122c1b93fb76f52d2465be2e8069e798e9d442"
-  integrity sha512-M3a10JCtTyKevb0MjuH6tU+cP/NVQZ82QPADqI1RQYY1OphztsCeIeQmTsHmF/NS6m0E51Zl4QNsI3odXSQF5w==
+babel-plugin-jest-hoist@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-25.5.0.tgz#129c80ba5c7fc75baf3a45b93e2e372d57ca2677"
+  integrity sha512-u+/W+WAjMlvoocYGTwthAiQSxDcJAyHpQ6oWlHdFZaaN+Rlk8Q7iiwDPg2lN/FyJtAYnKjFxbn7xus4HCFkg5g==
   dependencies:
+    "@babel/template" "^7.3.3"
+    "@babel/types" "^7.3.3"
     "@types/babel__traverse" "^7.0.6"
 
 babel-polyfill@^6.26.0:
@@ -1163,12 +1217,12 @@ babel-preset-current-node-syntax@^0.1.2:
     "@babel/plugin-syntax-optional-catch-binding" "^7.8.3"
     "@babel/plugin-syntax-optional-chaining" "^7.8.3"
 
-babel-preset-jest@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.4.0.tgz#10037cc32b751b994b260964629e49dc479abf4c"
-  integrity sha512-PwFiEWflHdu3JCeTr0Pb9NcHHE34qWFnPQRVPvqQITx4CsDCzs6o05923I10XvLvn9nNsRHuiVgB72wG/90ZHQ==
+babel-preset-jest@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/babel-preset-jest/-/babel-preset-jest-25.5.0.tgz#c1d7f191829487a907764c65307faa0e66590b49"
+  integrity sha512-8ZczygctQkBU+63DtSOKGh7tFL0CeCuz+1ieud9lJ1WPQ9O6A1a/r+LGn6Y705PA6whHQ3T1XuB/PmpfNYf8Fw==
   dependencies:
-    babel-plugin-jest-hoist "^25.4.0"
+    babel-plugin-jest-hoist "^25.5.0"
     babel-preset-current-node-syntax "^0.1.2"
 
 babel-runtime@^6.26.0:
@@ -1546,17 +1600,6 @@ chalk@*, chalk@4.0.0, chalk@^4.0.0:
     ansi-styles "^4.1.0"
     supports-color "^7.1.0"
 
-chalk@^1.0.0, chalk@^1.1.3:
-  version "1.1.3"
-  resolved "https://registry.yarnpkg.com/chalk/-/chalk-1.1.3.tgz#a8115c55e4a702fe4d150abd3872822a7e09fc98"
-  integrity sha1-qBFcVeSnAv5NFQq9OHKCKn4J/Jg=
-  dependencies:
-    ansi-styles "^2.2.1"
-    escape-string-regexp "^1.0.2"
-    has-ansi "^2.0.0"
-    strip-ansi "^3.0.0"
-    supports-color "^2.0.0"
-
 chalk@^2.0.0, chalk@^2.0.1, chalk@^2.3.0, chalk@^2.4.1, chalk@^2.4.2:
   version "2.4.2"
   resolved "https://registry.yarnpkg.com/chalk/-/chalk-2.4.2.tgz#cd42541677a54333cf541a49108c1432b44c9424"
@@ -1612,20 +1655,25 @@ class-utils@^0.3.5:
     isobject "^3.0.0"
     static-extend "^0.1.1"
 
-cli-cursor@^2.0.0, cli-cursor@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-2.1.0.tgz#b35dac376479facc3e94747d41d0d0f5238ffcb5"
-  integrity sha1-s12sN2R5+sw+lHR9QdDQ9SOP/LU=
+clean-stack@^2.0.0:
+  version "2.2.0"
+  resolved "https://registry.yarnpkg.com/clean-stack/-/clean-stack-2.2.0.tgz#ee8472dbb129e727b31e8a10a427dee9dfe4008b"
+  integrity sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==
+
+cli-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/cli-cursor/-/cli-cursor-3.1.0.tgz#264305a7ae490d1d03bf0c9ba7c925d1753af307"
+  integrity sha512-I/zHAwsKf9FqGoXM4WWRACob9+SNukZTd94DWF57E4toouRulbCxcUh6RKUEOQlYTHJnzkPMySvPNaaSLNfLZw==
   dependencies:
-    restore-cursor "^2.0.0"
+    restore-cursor "^3.1.0"
 
-cli-truncate@^0.2.1:
-  version "0.2.1"
-  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-0.2.1.tgz#9f15cfbb0705005369216c626ac7d05ab90dd574"
-  integrity sha1-nxXPuwcFAFNpIWxiasfQWrkN1XQ=
+cli-truncate@^2.1.0:
+  version "2.1.0"
+  resolved "https://registry.yarnpkg.com/cli-truncate/-/cli-truncate-2.1.0.tgz#c39e28bf05edcde5be3b98992a22deed5a2b93c7"
+  integrity sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==
   dependencies:
-    slice-ansi "0.0.4"
-    string-width "^1.0.1"
+    slice-ansi "^3.0.0"
+    string-width "^4.2.0"
 
 cliui@^5.0.0:
   version "5.0.0"
@@ -1660,11 +1708,6 @@ code-error-fragment@0.0.230:
   resolved "https://registry.yarnpkg.com/code-error-fragment/-/code-error-fragment-0.0.230.tgz#d736d75c832445342eca1d1fedbf17d9618b14d7"
   integrity sha512-cadkfKp6932H8UkhzE/gcUqhRMNf8jHzkAN7+5Myabswaghu4xABTgPHDCjW+dBAJxj/SpkTYokpzDqY4pCzQw==
 
-code-point-at@^1.0.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/code-point-at/-/code-point-at-1.1.0.tgz#0d070b4d043a5bea33a2f1a40e2edb3d9a4ccf77"
-  integrity sha1-DQcLTQQ6W+ozovGkDi7bPZpMz3c=
-
 collect-v8-coverage@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/collect-v8-coverage/-/collect-v8-coverage-1.0.0.tgz#150ee634ac3650b71d9c985eb7f608942334feb1"
@@ -1977,11 +2020,6 @@ data-urls@^1.1.0:
     whatwg-mimetype "^2.2.0"
     whatwg-url "^7.0.0"
 
-date-fns@^1.27.2:
-  version "1.30.1"
-  resolved "https://registry.yarnpkg.com/date-fns/-/date-fns-1.30.1.tgz#2e71bf0b119153dbb4cc4e88d9ea5acfb50dc05c"
-  integrity sha512-hBSVCvSmWC+QypYObzwGOd9wqdDpOt+0wl0KbU+R+uuZBS1jN8VsD1ss3irQDknRj5NvxiTF6oj/nDRnN/UQNw==
-
 date-format@^2.0.0, date-format@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/date-format/-/date-format-2.1.0.tgz#31d5b5ea211cf5fd764cd38baf9d033df7e125cf"
@@ -2057,7 +2095,7 @@ defaults@^1.0.3:
   dependencies:
     clone "^1.0.2"
 
-define-properties@^1.1.1, define-properties@^1.1.2, define-properties@^1.1.3:
+define-properties@^1.1.1:
   version "1.1.3"
   resolved "https://registry.yarnpkg.com/define-properties/-/define-properties-1.1.3.tgz#cf88da6cbee26fe6db7094f61d870cbd84cee9f1"
   integrity sha512-3MqfYKj2lLzdMSf8ZIZE/V+Zuy+BgD6f164e8K2w7dgnpKArBDerGYpM46IYYcjnkdPNMjPk9A6VFB8+3SKlXQ==
@@ -2220,10 +2258,10 @@ ee-first@1.1.1:
   resolved "https://registry.yarnpkg.com/ee-first/-/ee-first-1.1.1.tgz#590c61156b0ae2f4f0255732a158b266bc56b21d"
   integrity sha1-WQxhFWsK4vTwJVcyoViyZrxWsh0=
 
-elegant-spinner@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-1.0.1.tgz#db043521c95d7e303fd8f345bedc3349cfb0729e"
-  integrity sha1-2wQ1IcldfjA/2PNFvtwzSc+wcp4=
+elegant-spinner@^2.0.0:
+  version "2.0.0"
+  resolved "https://registry.yarnpkg.com/elegant-spinner/-/elegant-spinner-2.0.0.tgz#f236378985ecd16da75488d166be4b688fd5af94"
+  integrity sha512-5YRYHhvhYzV/FC4AiMdeSIg3jAYGq9xFvbhZMpPlJoBsfYgrw2DSCYeXfat6tYBu45PWiyRr3+flaCPPmviPaA==
 
 elliptic@^6.0.0:
   version "6.5.1"
@@ -2305,6 +2343,13 @@ engine.io@~3.2.0:
     engine.io-parser "~2.1.0"
     ws "~3.3.1"
 
+enquirer@^2.3.4:
+  version "2.3.5"
+  resolved "https://registry.yarnpkg.com/enquirer/-/enquirer-2.3.5.tgz#3ab2b838df0a9d8ab9e7dff235b0e8712ef92381"
+  integrity sha512-BNT1C08P9XD0vNg3J475yIUG+mVdp9T6towYFHUv897X0KoHBjB1shyrNmhmtHWKP17iSWgo7Gqh7BBuzLZMSA==
+  dependencies:
+    ansi-colors "^3.2.1"
+
 ent@~2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/ent/-/ent-2.2.0.tgz#e964219325a21d05f44466a2f686ed6ce5f5dd1d"
@@ -2322,50 +2367,6 @@ error-ex@^1.3.1:
   dependencies:
     is-arrayish "^0.2.1"
 
-es-abstract@^1.17.0-next.1, es-abstract@^1.17.4, es-abstract@^1.17.5:
-  version "1.17.5"
-  resolved "https://registry.yarnpkg.com/es-abstract/-/es-abstract-1.17.5.tgz#d8c9d1d66c8981fb9200e2251d799eee92774ae9"
-  integrity sha512-BR9auzDbySxOcfog0tLECW8l28eRGpDpU3Dm3Hp4q/N+VtLTmyj4EUN088XZWQDW/hzj6sYRDXeOFsaAODKvpg==
-  dependencies:
-    es-to-primitive "^1.2.1"
-    function-bind "^1.1.1"
-    has "^1.0.3"
-    has-symbols "^1.0.1"
-    is-callable "^1.1.5"
-    is-regex "^1.0.5"
-    object-inspect "^1.7.0"
-    object-keys "^1.1.1"
-    object.assign "^4.1.0"
-    string.prototype.trimleft "^2.1.1"
-    string.prototype.trimright "^2.1.1"
-
-es-array-method-boxes-properly@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/es-array-method-boxes-properly/-/es-array-method-boxes-properly-1.0.0.tgz#873f3e84418de4ee19c5be752990b2e44718d09e"
-  integrity sha512-wd6JXUmyHmt8T5a2xreUwKcGPq6f1f+WwIJkijUqiGcJz1qqnZgP6XIK+QyIWU5lT7imeNxUll48bziG+TSYcA==
-
-es-get-iterator@^1.0.2:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/es-get-iterator/-/es-get-iterator-1.1.0.tgz#bb98ad9d6d63b31aacdc8f89d5d0ee57bcb5b4c8"
-  integrity sha512-UfrmHuWQlNMTs35e1ypnvikg6jCz3SK8v8ImvmDsh36fCVUR1MqoFDiyn0/k52C8NqO3YsO8Oe0azeesNuqSsQ==
-  dependencies:
-    es-abstract "^1.17.4"
-    has-symbols "^1.0.1"
-    is-arguments "^1.0.4"
-    is-map "^2.0.1"
-    is-set "^2.0.1"
-    is-string "^1.0.5"
-    isarray "^2.0.5"
-
-es-to-primitive@^1.2.1:
-  version "1.2.1"
-  resolved "https://registry.yarnpkg.com/es-to-primitive/-/es-to-primitive-1.2.1.tgz#e55cd4c9cdc188bcefb03b366c736323fc5c898a"
-  integrity sha512-QCOllgZJtaUo9miYBcLChTUaHNjJF3PYs1VidD7AwiEj1kYxKeQTctLAezAOH5ZKRH0g2IgPn6KwB4IT8iRpvA==
-  dependencies:
-    is-callable "^1.1.4"
-    is-date-object "^1.0.1"
-    is-symbol "^1.0.2"
-
 es6-object-assign@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/es6-object-assign/-/es6-object-assign-1.1.0.tgz#c2c3582656247c39ea107cb1e6652b6f9f24523c"
@@ -2388,7 +2389,7 @@ escape-html@~1.0.3:
   resolved "https://registry.yarnpkg.com/escape-html/-/escape-html-1.0.3.tgz#0258eae4d3d0c0974de1c169188ef0051d1d1988"
   integrity sha1-Aljq5NPQwJdN4cFpGI7wBR0dGYg=
 
-escape-string-regexp@^1.0.2, escape-string-regexp@^1.0.5:
+escape-string-regexp@^1.0.5:
   version "1.0.5"
   resolved "https://registry.yarnpkg.com/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz#1b61c0562190a8dff6ae3bb2cf0200ca130b86d4"
   integrity sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=
@@ -2571,16 +2572,16 @@ expect@^24.8.0:
     jest-message-util "^24.9.0"
     jest-regex-util "^24.9.0"
 
-expect@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/expect/-/expect-25.4.0.tgz#0b16c17401906d1679d173e59f0d4580b22f8dc8"
-  integrity sha512-7BDIX99BTi12/sNGJXA9KMRcby4iAmu1xccBOhyKCyEhjcVKS3hPmHdA/4nSI9QGIOkUropKqr3vv7WMDM5lvQ==
+expect@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/expect/-/expect-25.5.0.tgz#f07f848712a2813bb59167da3fb828ca21f58bba"
+  integrity sha512-w7KAXo0+6qqZZhovCaBVPSIqQp7/UTcx4M9uKt2m6pd2VB1voyC8JizLRqeEqud3AAVP02g+hbErDu5gu64tlA==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     ansi-styles "^4.0.0"
     jest-get-type "^25.2.6"
-    jest-matcher-utils "^25.4.0"
-    jest-message-util "^25.4.0"
+    jest-matcher-utils "^25.5.0"
+    jest-message-util "^25.5.0"
     jest-regex-util "^25.2.6"
 
 export-files@^2.0.1:
@@ -2710,18 +2711,10 @@ fetch-mock@^7.7.3:
     path-to-regexp "^2.2.1"
     whatwg-url "^6.5.0"
 
-figures@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/figures/-/figures-1.7.0.tgz#cbe1e3affcf1cd44b80cadfed28dc793a9701d2e"
-  integrity sha1-y+Hjr/zxzUS4DK3+0o3Hk6lwHS4=
-  dependencies:
-    escape-string-regexp "^1.0.5"
-    object-assign "^4.1.0"
-
-figures@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/figures/-/figures-2.0.0.tgz#3ab1a2d2a62c8bfb431a0c94cb797a2fce27c962"
-  integrity sha1-OrGi0qYsi/tDGgyUy3l6L84nyWI=
+figures@^3.2.0:
+  version "3.2.0"
+  resolved "https://registry.yarnpkg.com/figures/-/figures-3.2.0.tgz#625c18bd293c604dc4a8ddb2febf0c88341746af"
+  integrity sha512-yaduQFRKLXYOGgEn6AZau90j3ggSOyiqXU0F9JZfeXYhNa+Jk4X+s45A2zg5jns87GAFa34BBm2kXw4XpNcbdg==
   dependencies:
     escape-string-regexp "^1.0.5"
 
@@ -2916,11 +2909,6 @@ ftp@~0.3.10:
     readable-stream "1.1.x"
     xregexp "2.0.0"
 
-function-bind@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/function-bind/-/function-bind-1.1.1.tgz#a56899d3ea3c9bab874bb9773b7c5ede92f4895d"
-  integrity sha512-yIovAzMX49sF8Yl58fSCWJ5svSLuaibPxXQJFLmBObTuCr0Mf1KiPopGM9NiFjiYBCbfaa2Fh6breQ6ANVTI0A==
-
 gensync@^1.0.0-beta.1:
   version "1.0.0-beta.1"
   resolved "https://registry.yarnpkg.com/gensync/-/gensync-1.0.0-beta.1.tgz#58f4361ff987e5ff6e1e7a210827aa371eaac269"
@@ -3122,11 +3110,16 @@ globby@^11.0.0:
     merge2 "^1.3.0"
     slash "^3.0.0"
 
-graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0, graceful-fs@^4.2.3:
+graceful-fs@^4.1.15, graceful-fs@^4.1.2, graceful-fs@^4.1.6, graceful-fs@^4.2.0:
   version "4.2.3"
   resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.3.tgz#4a12ff1b60376ef09862c2093edd908328be8423"
   integrity sha512-a30VEBm4PEdx1dRB7MFK7BejejvCvBronbLjht+sHuGYj8PHs7M/5Z+rt5lw551vZ7yfTCj4Vuyy3mSJytDWRQ==
 
+graceful-fs@^4.2.4:
+  version "4.2.4"
+  resolved "https://registry.yarnpkg.com/graceful-fs/-/graceful-fs-4.2.4.tgz#2256bde14d3632958c465ebc96dc467ca07a29fb"
+  integrity sha512-WjKPNJF79dtJAVniUlGGWHYGz2jWxT6VhN/4m1NdkbZ2nOsEF+cI1Edgql5zCRhs/VsQYRvrXctxktVXZUkixw==
+
 grapheme-splitter@^1.0.4:
   version "1.0.4"
   resolved "https://registry.yarnpkg.com/grapheme-splitter/-/grapheme-splitter-1.0.4.tgz#9cf3a665c6247479896834af35cf1dbb4400767e"
@@ -3150,13 +3143,6 @@ har-validator@~5.1.0:
     ajv "^6.5.5"
     har-schema "^2.0.0"
 
-has-ansi@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/has-ansi/-/has-ansi-2.0.0.tgz#34f5049ce1ecdf2b0649af3ef24e45ed35416d91"
-  integrity sha1-NPUEnOHs3ysGSa8+8k5F7TVBbZE=
-  dependencies:
-    ansi-regex "^2.0.0"
-
 has-binary2@~1.0.2:
   version "1.0.3"
   resolved "https://registry.yarnpkg.com/has-binary2/-/has-binary2-1.0.3.tgz#7776ac627f3ea77250cfc332dab7ddf5e4f5d11d"
@@ -3179,11 +3165,6 @@ has-flag@^4.0.0:
   resolved "https://registry.yarnpkg.com/has-flag/-/has-flag-4.0.0.tgz#944771fd9c81c81265c4d6941860da06bb59479b"
   integrity sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==
 
-has-symbols@^1.0.0, has-symbols@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/has-symbols/-/has-symbols-1.0.1.tgz#9f5214758a44196c406d9bd76cebf81ec2dd31e8"
-  integrity sha512-PLcsoqu++dmEIZB+6totNFKq/7Do+Z0u4oT0zKOJNl3lYK6vGwwu2hjHs+68OEZbTjiUE9bgOABXbP/GvrS0Kg==
-
 has-value@^0.3.1:
   version "0.3.1"
   resolved "https://registry.yarnpkg.com/has-value/-/has-value-0.3.1.tgz#7b1f58bada62ca827ec0a2078025654845995e1f"
@@ -3215,13 +3196,6 @@ has-values@^1.0.0:
     is-number "^3.0.0"
     kind-of "^4.0.0"
 
-has@^1.0.3:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/has/-/has-1.0.3.tgz#722d7cbfc1f6aa8241f16dd814e011e1f41e8796"
-  integrity sha512-f2dvO0VU6Oej7RkWJGrehjbzMAjFp5/VKPp5tTpWIV4JHHZK1/BxbFRtf/siA2SWTe09caDmVtYYzWEIbBS4zw==
-  dependencies:
-    function-bind "^1.1.1"
-
 hash-base@^3.0.0:
   version "3.0.4"
   resolved "https://registry.yarnpkg.com/hash-base/-/hash-base-3.0.4.tgz#5fc8686847ecd73499403319a6b0a3f3f6ae4918"
@@ -3390,10 +3364,10 @@ imurmurhash@^0.1.4:
   resolved "https://registry.yarnpkg.com/imurmurhash/-/imurmurhash-0.1.4.tgz#9218b9b2b928a238b13dc4fb6b6d576f231453ea"
   integrity sha1-khi5srkoojixPcT7a21XbyMUU+o=
 
-indent-string@^3.0.0:
-  version "3.2.0"
-  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-3.2.0.tgz#4a5fd6d27cc332f37e5419a504dbb837105c9289"
-  integrity sha1-Sl/W0nzDMvN+VBmlBNu4NxBckok=
+indent-string@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/indent-string/-/indent-string-4.0.0.tgz#624f8f4497d619b2d9768531d58f4122854d7251"
+  integrity sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==
 
 indexof@0.0.1:
   version "0.0.1"
@@ -3492,11 +3466,6 @@ is-buffer@^1.0.2, is-buffer@^1.1.5:
   resolved "https://registry.yarnpkg.com/is-buffer/-/is-buffer-1.1.6.tgz#efaa2ea9daa0d7ab2ea13a97b2b8ad51fefbe8be"
   integrity sha512-NcdALwpXkTm5Zvvbk7owOUSvVvBKDgKP5/ewfXEznmQFfs4ZRmanOeKBTjRVjka3QFoN6XJ+9F3USqfHqTaU5w==
 
-is-callable@^1.1.4, is-callable@^1.1.5:
-  version "1.1.5"
-  resolved "https://registry.yarnpkg.com/is-callable/-/is-callable-1.1.5.tgz#f7e46b596890456db74e7f6e976cb3273d06faab"
-  integrity sha512-ESKv5sMCJB2jnHTWZ3O5itG+O128Hsus4K4Qh1h2/cgn2vbgnLSVqfV46AeJA9D5EeeLa9w81KUXMtn34zhX+Q==
-
 is-ci@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-ci/-/is-ci-2.0.0.tgz#6bc6334181810e04b5c22b3d589fdca55026404c"
@@ -3518,11 +3487,6 @@ is-data-descriptor@^1.0.0:
   dependencies:
     kind-of "^6.0.0"
 
-is-date-object@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/is-date-object/-/is-date-object-1.0.2.tgz#bda736f2cd8fd06d32844e7743bfa7494c3bfd7e"
-  integrity sha512-USlDT524woQ08aoZFzh3/Z6ch9Y/EWXEHQ/AaRN0SkKq4t2Jw2R2339tSXmwuVoY7LLlBCbOIlx2myP/L5zk0g==
-
 is-descriptor@^0.1.0:
   version "0.1.6"
   resolved "https://registry.yarnpkg.com/is-descriptor/-/is-descriptor-0.1.6.tgz#366d8240dde487ca51823b1ab9f07a10a78251ca"
@@ -3582,13 +3546,6 @@ is-extglob@^2.1.1:
   resolved "https://registry.yarnpkg.com/is-extglob/-/is-extglob-2.1.1.tgz#a88c02535791f02ed37c76a1b9ea9773c833f8c2"
   integrity sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=
 
-is-fullwidth-code-point@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-1.0.0.tgz#ef9e31386f031a7f0d643af82fde50c457ef00cb"
-  integrity sha1-754xOG8DGn8NZDr4L95QxFfvAMs=
-  dependencies:
-    number-is-nan "^1.0.0"
-
 is-fullwidth-code-point@^2.0.0:
   version "2.0.0"
   resolved "https://registry.yarnpkg.com/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz#a3b30a5c4f199183167aaab93beefae3ddfb654f"
@@ -3628,11 +3585,6 @@ is-glob@^4.0.1, is-glob@~4.0.1:
   dependencies:
     is-extglob "^2.1.1"
 
-is-map@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.1.tgz#520dafc4307bb8ebc33b813de5ce7c9400d644a1"
-  integrity sha512-T/S49scO8plUiAOA2DBTBG3JHpn1yiw0kRp6dgiZ0v2/6twi5eiB0rHtHFH9ZIrvlWc6+4O+m4zg5+Z833aXgw==
-
 is-module@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591"
@@ -3674,13 +3626,6 @@ is-obj@^1.0.0, is-obj@^1.0.1:
   resolved "https://registry.yarnpkg.com/is-obj/-/is-obj-1.0.1.tgz#3e4729ac1f5fde025cd7d83a896dab9f4f67db0f"
   integrity sha1-PkcprB9f3gJc19g6iW2rn09n2w8=
 
-is-observable@^1.1.0:
-  version "1.1.0"
-  resolved "https://registry.yarnpkg.com/is-observable/-/is-observable-1.1.0.tgz#b3e986c8f44de950867cab5403f5a3465005975e"
-  integrity sha512-NqCa4Sa2d+u7BWc6CukaObG3Fh+CU9bvixbpcXYhy2VvYS7vVGIdAgnIS5Ks3A/cqk4rebLJ9s8zBstT2aKnIA==
-  dependencies:
-    symbol-observable "^1.1.0"
-
 is-plain-object@^2.0.3, is-plain-object@^2.0.4:
   version "2.0.4"
   resolved "https://registry.yarnpkg.com/is-plain-object/-/is-plain-object-2.0.4.tgz#2c163b3fafb1b606d9d17928f05c2a1c38e07677"
@@ -3698,11 +3643,6 @@ is-primitive@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-primitive/-/is-primitive-2.0.0.tgz#207bab91638499c07b2adf240a41a87210034575"
   integrity sha1-IHurkWOEmcB7Kt8kCkGochADRXU=
 
-is-promise@^2.1.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/is-promise/-/is-promise-2.1.0.tgz#79a2a9ece7f096e80f36d2b2f3bc16c1ff4bf3fa"
-  integrity sha1-eaKp7OfwlugPNtKy87wWwf9L8/o=
-
 is-reference@^1.1.2:
   version "1.1.4"
   resolved "https://registry.yarnpkg.com/is-reference/-/is-reference-1.1.4.tgz#3f95849886ddb70256a3e6d062b1a68c13c51427"
@@ -3710,13 +3650,6 @@ is-reference@^1.1.2:
   dependencies:
     "@types/estree" "0.0.39"
 
-is-regex@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/is-regex/-/is-regex-1.0.5.tgz#39d589a358bf18967f726967120b8fc1aed74eae"
-  integrity sha512-vlKW17SNq44owv5AQR3Cq0bQPEb8+kF3UKZ2fiZNOWtztYE5i0CzCZxFDwO58qAOWtxdBRVO/V5Qin1wjCqFYQ==
-  dependencies:
-    has "^1.0.3"
-
 is-regexp@^1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-regexp/-/is-regexp-1.0.0.tgz#fd2d883545c46bac5a633e7b9a09e87fa2cb5069"
@@ -3729,11 +3662,6 @@ is-relative@^0.2.1:
   dependencies:
     is-unc-path "^0.1.1"
 
-is-set@^2.0.1:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/is-set/-/is-set-2.0.1.tgz#d1604afdab1724986d30091575f54945da7e5f43"
-  integrity sha512-eJEzOtVyenDs1TMzSQ3kU3K+E0GUS9sno+F0OBT97xsgcJsF9nXMBtkT9/kut5JEpM7oL7X/0qxR17K3mcwIAA==
-
 is-stream@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-1.1.0.tgz#12d4a3dd4e68e0b79ceb8dbc84173ae80d91ca44"
@@ -3744,18 +3672,6 @@ is-stream@^2.0.0:
   resolved "https://registry.yarnpkg.com/is-stream/-/is-stream-2.0.0.tgz#bde9c32680d6fae04129d6ac9d921ce7815f78e3"
   integrity sha512-XCoy+WlUr7d1+Z8GgSuXmpuUFC9fOhRXglJMx+dwLKTkL44Cjd4W1Z5P+BQZpr+cR93aGP4S/s7Ftw6Nd/kiEw==
 
-is-string@^1.0.4, is-string@^1.0.5:
-  version "1.0.5"
-  resolved "https://registry.yarnpkg.com/is-string/-/is-string-1.0.5.tgz#40493ed198ef3ff477b8c7f92f644ec82a5cd3a6"
-  integrity sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==
-
-is-symbol@^1.0.2:
-  version "1.0.3"
-  resolved "https://registry.yarnpkg.com/is-symbol/-/is-symbol-1.0.3.tgz#38e1014b9e6329be0de9d24a414fd7441ec61937"
-  integrity sha512-OwijhaRSgqvhm/0ZdAcXNZt9lYdKFpcRDT5ULUuYXPoT794UNOdU+gpT6Rzo7b4V2HUl/op6GqY894AZwv9faQ==
-  dependencies:
-    has-symbols "^1.0.1"
-
 is-typedarray@^1.0.0, is-typedarray@~1.0.0:
   version "1.0.0"
   resolved "https://registry.yarnpkg.com/is-typedarray/-/is-typedarray-1.0.0.tgz#e479c80858df0c1b11ddda6940f96011fcda4a9a"
@@ -3803,11 +3719,6 @@ isarray@2.0.1:
   resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.1.tgz#a37d94ed9cda2d59865c9f76fe596ee1f338741e"
   integrity sha1-o32U7ZzaLVmGXJ92/llu4fM4dB4=
 
-isarray@^2.0.5:
-  version "2.0.5"
-  resolved "https://registry.yarnpkg.com/isarray/-/isarray-2.0.5.tgz#8af1e4c1221244cc62459faf38940d4e644a5723"
-  integrity sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==
-
 isbinaryfile@^3.0.0:
   version "3.0.3"
   resolved "https://registry.yarnpkg.com/isbinaryfile/-/isbinaryfile-3.0.3.tgz#5d6def3edebf6e8ca8cae9c30183a804b5f8be80"
@@ -3886,74 +3797,63 @@ istanbul-reports@^3.0.0, istanbul-reports@^3.0.2:
     html-escaper "^2.0.0"
     istanbul-lib-report "^3.0.0"
 
-iterate-iterator@^1.0.1:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/iterate-iterator/-/iterate-iterator-1.0.1.tgz#1693a768c1ddd79c969051459453f082fe82e9f6"
-  integrity sha512-3Q6tudGN05kbkDQDI4CqjaBf4qf85w6W6GnuZDtUVYwKgtC1q8yxYX7CZed7N+tLzQqS6roujWvszf13T+n9aw==
-
-iterate-value@^1.0.0:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/iterate-value/-/iterate-value-1.0.2.tgz#935115bd37d006a52046535ebc8d07e9c9337f57"
-  integrity sha512-A6fMAio4D2ot2r/TYzr4yUWrmwNdsN5xL7+HUiyACE4DXm+q8HtPcnFTp+NnW3k4N05tZ7FVYFFb2CR13NxyHQ==
-  dependencies:
-    es-get-iterator "^1.0.2"
-    iterate-iterator "^1.0.1"
-
 jasmine-core@^3.5.0:
   version "3.5.0"
   resolved "https://registry.yarnpkg.com/jasmine-core/-/jasmine-core-3.5.0.tgz#132c23e645af96d85c8bca13c8758b18429fc1e4"
   integrity sha512-nCeAiw37MIMA9w9IXso7bRaLl+c/ef3wnxsoSAlYrzS+Ot0zTG6nU8G/cIfGkqpkjX2wNaIW9RFG0TwIFnG6bA==
 
-jest-changed-files@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.4.0.tgz#e573db32c2fd47d2b90357ea2eda0622c5c5cbd6"
-  integrity sha512-VR/rfJsEs4BVMkwOTuStRyS630fidFVekdw/lBaBQjx9KK3VZFOZ2c0fsom2fRp8pMCrCTP6LGna00o/DXGlqA==
+jest-changed-files@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-changed-files/-/jest-changed-files-25.5.0.tgz#141cc23567ceb3f534526f8614ba39421383634c"
+  integrity sha512-EOw9QEqapsDT7mKF162m8HFzRPbmP8qJQny6ldVOdOVBz3ACgPm/1nAn5fPQ/NDaYhX/AHkrGwwkCncpAVSXcw==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     execa "^3.2.0"
     throat "^5.0.0"
 
-jest-cli@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.4.0.tgz#5dac8be0fece6ce39f0d671395a61d1357322bab"
-  integrity sha512-usyrj1lzCJZMRN1r3QEdnn8e6E6yCx/QN7+B1sLoA68V7f3WlsxSSQfy0+BAwRiF4Hz2eHauf11GZG3PIfWTXQ==
+jest-cli@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-cli/-/jest-cli-25.5.4.tgz#b9f1a84d1301a92c5c217684cb79840831db9f0d"
+  integrity sha512-rG8uJkIiOUpnREh1768/N3n27Cm+xPFkSNFO91tgg+8o2rXeVLStz+vkXkGr4UtzH6t1SNbjwoiswd7p4AhHTw==
   dependencies:
-    "@jest/core" "^25.4.0"
-    "@jest/test-result" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/core" "^25.5.4"
+    "@jest/test-result" "^25.5.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
     exit "^0.1.2"
+    graceful-fs "^4.2.4"
     import-local "^3.0.2"
     is-ci "^2.0.0"
-    jest-config "^25.4.0"
-    jest-util "^25.4.0"
-    jest-validate "^25.4.0"
+    jest-config "^25.5.4"
+    jest-util "^25.5.0"
+    jest-validate "^25.5.0"
     prompts "^2.0.1"
     realpath-native "^2.0.0"
     yargs "^15.3.1"
 
-jest-config@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.4.0.tgz#56e5df3679a96ff132114b44fb147389c8c0a774"
-  integrity sha512-egT9aKYxMyMSQV1aqTgam0SkI5/I2P9qrKexN5r2uuM2+68ypnc+zPGmfUxK7p1UhE7dYH9SLBS7yb+TtmT1AA==
+jest-config@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-config/-/jest-config-25.5.4.tgz#38e2057b3f976ef7309b2b2c8dcd2a708a67f02c"
+  integrity sha512-SZwR91SwcdK6bz7Gco8qL7YY2sx8tFJYzvg216DLihTWf+LKY/DoJXpM9nTzYakSyfblbqeU48p/p7Jzy05Atg==
   dependencies:
     "@babel/core" "^7.1.0"
-    "@jest/test-sequencer" "^25.4.0"
-    "@jest/types" "^25.4.0"
-    babel-jest "^25.4.0"
+    "@jest/test-sequencer" "^25.5.4"
+    "@jest/types" "^25.5.0"
+    babel-jest "^25.5.1"
     chalk "^3.0.0"
     deepmerge "^4.2.2"
     glob "^7.1.1"
-    jest-environment-jsdom "^25.4.0"
-    jest-environment-node "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-environment-jsdom "^25.5.0"
+    jest-environment-node "^25.5.0"
     jest-get-type "^25.2.6"
-    jest-jasmine2 "^25.4.0"
+    jest-jasmine2 "^25.5.4"
     jest-regex-util "^25.2.6"
-    jest-resolve "^25.4.0"
-    jest-util "^25.4.0"
-    jest-validate "^25.4.0"
+    jest-resolve "^25.5.1"
+    jest-util "^25.5.0"
+    jest-validate "^25.5.0"
     micromatch "^4.0.2"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
     realpath-native "^2.0.0"
 
 jest-diff@^24.9.0:
@@ -3967,24 +3867,24 @@ jest-diff@^24.9.0:
     pretty-format "^24.9.0"
 
 jest-diff@^25.2.1:
-  version "25.3.0"
-  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.3.0.tgz#0d7d6f5d6171e5dacde9e05be47b3615e147c26f"
-  integrity sha512-vyvs6RPoVdiwARwY4kqFWd4PirPLm2dmmkNzKqo38uZOzJvLee87yzDjIZLmY1SjM3XR5DwsUH+cdQ12vgqi1w==
+  version "25.4.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.4.0.tgz#260b70f19a46c283adcad7f081cae71eb784a634"
+  integrity sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==
   dependencies:
     chalk "^3.0.0"
     diff-sequences "^25.2.6"
     jest-get-type "^25.2.6"
-    pretty-format "^25.3.0"
+    pretty-format "^25.4.0"
 
-jest-diff@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.4.0.tgz#260b70f19a46c283adcad7f081cae71eb784a634"
-  integrity sha512-kklLbJVXW0y8UKOWOdYhI6TH5MG6QAxrWiBMgQaPIuhj3dNFGirKCd+/xfplBXICQ7fI+3QcqHm9p9lWu1N6ug==
+jest-diff@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-diff/-/jest-diff-25.5.0.tgz#1dd26ed64f96667c068cef026b677dfa01afcfa9"
+  integrity sha512-z1kygetuPiREYdNIumRpAHY6RXiGmp70YHptjdaxTWGmA085W3iCnXNx0DhflK3vwrKmrRWyY1wUpkPMVxMK7A==
   dependencies:
     chalk "^3.0.0"
     diff-sequences "^25.2.6"
     jest-get-type "^25.2.6"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
 
 jest-docblock@^21.0.0:
   version "21.2.0"
@@ -3998,39 +3898,39 @@ jest-docblock@^25.3.0:
   dependencies:
     detect-newline "^3.0.0"
 
-jest-each@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.4.0.tgz#ad4e46164764e8e77058f169a0076a7f86f6b7d4"
-  integrity sha512-lwRIJ8/vQU/6vq3nnSSUw1Y3nz5tkYSFIywGCZpUBd6WcRgpn8NmJoQICojbpZmsJOJNHm0BKdyuJ6Xdx+eDQQ==
+jest-each@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-each/-/jest-each-25.5.0.tgz#0c3c2797e8225cb7bec7e4d249dcd96b934be516"
+  integrity sha512-QBogUxna3D8vtiItvn54xXde7+vuzqRrEeaw8r1s+1TG9eZLVJE5ZkKoSUlqFwRjnlaA4hyKGiu9OlkFIuKnjA==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
     jest-get-type "^25.2.6"
-    jest-util "^25.4.0"
-    pretty-format "^25.4.0"
-
-jest-environment-jsdom@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.4.0.tgz#bbfc7f85bb6ade99089062a830c79cb454565cf0"
-  integrity sha512-KTitVGMDrn2+pt7aZ8/yUTuS333w3pWt1Mf88vMntw7ZSBNDkRS6/4XLbFpWXYfWfp1FjcjQTOKzbK20oIehWQ==
-  dependencies:
-    "@jest/environment" "^25.4.0"
-    "@jest/fake-timers" "^25.4.0"
-    "@jest/types" "^25.4.0"
-    jest-mock "^25.4.0"
-    jest-util "^25.4.0"
+    jest-util "^25.5.0"
+    pretty-format "^25.5.0"
+
+jest-environment-jsdom@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-jsdom/-/jest-environment-jsdom-25.5.0.tgz#dcbe4da2ea997707997040ecf6e2560aec4e9834"
+  integrity sha512-7Jr02ydaq4jaWMZLY+Skn8wL5nVIYpWvmeatOHL3tOcV3Zw8sjnPpx+ZdeBfc457p8jCR9J6YCc+Lga0oIy62A==
+  dependencies:
+    "@jest/environment" "^25.5.0"
+    "@jest/fake-timers" "^25.5.0"
+    "@jest/types" "^25.5.0"
+    jest-mock "^25.5.0"
+    jest-util "^25.5.0"
     jsdom "^15.2.1"
 
-jest-environment-node@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.4.0.tgz#188aef01ae6418e001c03fdd1c299961e1439082"
-  integrity sha512-wryZ18vsxEAKFH7Z74zi/y/SyI1j6UkVZ6QsllBuT/bWlahNfQjLNwFsgh/5u7O957dYFoXj4yfma4n4X6kU9A==
+jest-environment-node@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-environment-node/-/jest-environment-node-25.5.0.tgz#0f55270d94804902988e64adca37c6ce0f7d07a1"
+  integrity sha512-iuxK6rQR2En9EID+2k+IBs5fCFd919gVVK5BeND82fYeLWPqvRcFNPKu9+gxTwfB5XwBGBvZ0HFQa+cHtIoslA==
   dependencies:
-    "@jest/environment" "^25.4.0"
-    "@jest/fake-timers" "^25.4.0"
-    "@jest/types" "^25.4.0"
-    jest-mock "^25.4.0"
-    jest-util "^25.4.0"
+    "@jest/environment" "^25.5.0"
+    "@jest/fake-timers" "^25.5.0"
+    "@jest/types" "^25.5.0"
+    jest-mock "^25.5.0"
+    jest-util "^25.5.0"
     semver "^6.3.0"
 
 jest-get-type@^24.9.0:
@@ -4043,18 +3943,19 @@ jest-get-type@^25.2.6:
   resolved "https://registry.yarnpkg.com/jest-get-type/-/jest-get-type-25.2.6.tgz#0b0a32fab8908b44d508be81681487dbabb8d877"
   integrity sha512-DxjtyzOHjObRM+sM1knti6or+eOgcGU4xVSb2HNP1TqO4ahsT+rqZg+nyqHWJSvWgKC5cG3QjGFBqxLghiF/Ig==
 
-jest-haste-map@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.4.0.tgz#da7c309dd7071e0a80c953ba10a0ec397efb1ae2"
-  integrity sha512-5EoCe1gXfGC7jmXbKzqxESrgRcaO3SzWXGCnvp9BcT0CFMyrB1Q6LIsjl9RmvmJGQgW297TCfrdgiy574Rl9HQ==
+jest-haste-map@^25.5.1:
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/jest-haste-map/-/jest-haste-map-25.5.1.tgz#1df10f716c1d94e60a1ebf7798c9fb3da2620943"
+  integrity sha512-dddgh9UZjV7SCDQUrQ+5t9yy8iEgKc1AKqZR9YDww8xsVOtzPQSMVLDChc21+g29oTRexb9/B0bIlZL+sWmvAQ==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
+    "@types/graceful-fs" "^4.1.2"
     anymatch "^3.0.3"
     fb-watchman "^2.0.0"
-    graceful-fs "^4.2.3"
-    jest-serializer "^25.2.6"
-    jest-util "^25.4.0"
-    jest-worker "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-serializer "^25.5.0"
+    jest-util "^25.5.0"
+    jest-worker "^25.5.0"
     micromatch "^4.0.2"
     sane "^4.0.3"
     walker "^1.0.7"
@@ -4062,36 +3963,36 @@ jest-haste-map@^25.4.0:
   optionalDependencies:
     fsevents "^2.1.2"
 
-jest-jasmine2@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.4.0.tgz#3d3d19514022e2326e836c2b66d68b4cb63c5861"
-  integrity sha512-QccxnozujVKYNEhMQ1vREiz859fPN/XklOzfQjm2j9IGytAkUbSwjFRBtQbHaNZ88cItMpw02JnHGsIdfdpwxQ==
+jest-jasmine2@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-jasmine2/-/jest-jasmine2-25.5.4.tgz#66ca8b328fb1a3c5364816f8958f6970a8526968"
+  integrity sha512-9acbWEfbmS8UpdcfqnDO+uBUgKa/9hcRh983IHdM+pKmJPL77G0sWAAK0V0kr5LK3a8cSBfkFSoncXwQlRZfkQ==
   dependencies:
     "@babel/traverse" "^7.1.0"
-    "@jest/environment" "^25.4.0"
-    "@jest/source-map" "^25.2.6"
-    "@jest/test-result" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/environment" "^25.5.0"
+    "@jest/source-map" "^25.5.0"
+    "@jest/test-result" "^25.5.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
     co "^4.6.0"
-    expect "^25.4.0"
+    expect "^25.5.0"
     is-generator-fn "^2.0.0"
-    jest-each "^25.4.0"
-    jest-matcher-utils "^25.4.0"
-    jest-message-util "^25.4.0"
-    jest-runtime "^25.4.0"
-    jest-snapshot "^25.4.0"
-    jest-util "^25.4.0"
-    pretty-format "^25.4.0"
+    jest-each "^25.5.0"
+    jest-matcher-utils "^25.5.0"
+    jest-message-util "^25.5.0"
+    jest-runtime "^25.5.4"
+    jest-snapshot "^25.5.1"
+    jest-util "^25.5.0"
+    pretty-format "^25.5.0"
     throat "^5.0.0"
 
-jest-leak-detector@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.4.0.tgz#cf94a160c78e53d810e7b2f40b5fd7ee263375b3"
-  integrity sha512-7Y6Bqfv2xWsB+7w44dvZuLs5SQ//fzhETgOGG7Gq3TTGFdYvAgXGwV8z159RFZ6fXiCPm/szQ90CyfVos9JIFQ==
+jest-leak-detector@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-leak-detector/-/jest-leak-detector-25.5.0.tgz#2291c6294b0ce404241bb56fe60e2d0c3e34f0bb"
+  integrity sha512-rV7JdLsanS8OkdDpZtgBf61L5xZ4NnYLBq72r6ldxahJWWczZjXawRsoHyXzibM5ed7C2QRjpp6ypgwGdKyoVA==
   dependencies:
     jest-get-type "^25.2.6"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
 
 jest-matcher-utils@^24.9.0:
   version "24.9.0"
@@ -4103,15 +4004,15 @@ jest-matcher-utils@^24.9.0:
     jest-get-type "^24.9.0"
     pretty-format "^24.9.0"
 
-jest-matcher-utils@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.4.0.tgz#dc3e7aec402a1e567ed80b572b9ad285878895e6"
-  integrity sha512-yPMdtj7YDgXhnGbc66bowk8AkQ0YwClbbwk3Kzhn5GVDrciiCr27U4NJRbrqXbTdtxjImONITg2LiRIw650k5A==
+jest-matcher-utils@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-matcher-utils/-/jest-matcher-utils-25.5.0.tgz#fbc98a12d730e5d2453d7f1ed4a4d948e34b7867"
+  integrity sha512-VWI269+9JS5cpndnpCwm7dy7JtGQT30UHfrnM3mXl22gHGt/b7NkjBqXfbhZ8V4B7ANUsjK18PlSBmG0YH7gjw==
   dependencies:
     chalk "^3.0.0"
-    jest-diff "^25.4.0"
+    jest-diff "^25.5.0"
     jest-get-type "^25.2.6"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
 
 jest-message-util@^24.9.0:
   version "24.9.0"
@@ -4127,25 +4028,33 @@ jest-message-util@^24.9.0:
     slash "^2.0.0"
     stack-utils "^1.0.1"
 
-jest-message-util@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.4.0.tgz#2899e8bc43f5317acf8dfdfe89ea237d354fcdab"
-  integrity sha512-LYY9hRcVGgMeMwmdfh9tTjeux1OjZHMusq/E5f3tJN+dAoVVkJtq5ZUEPIcB7bpxDUt2zjUsrwg0EGgPQ+OhXQ==
+jest-message-util@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-message-util/-/jest-message-util-25.5.0.tgz#ea11d93204cc7ae97456e1d8716251185b8880ea"
+  integrity sha512-ezddz3YCT/LT0SKAmylVyWWIGYoKHOFOFXx3/nA4m794lfVUskMcwhip6vTgdVrOtYdjeQeis2ypzes9mZb4EA==
   dependencies:
     "@babel/code-frame" "^7.0.0"
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     "@types/stack-utils" "^1.0.1"
     chalk "^3.0.0"
+    graceful-fs "^4.2.4"
     micromatch "^4.0.2"
     slash "^3.0.0"
     stack-utils "^1.0.1"
 
-jest-mock@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.4.0.tgz#ded7d64b5328d81d78d2138c825d3a45e30ec8ca"
-  integrity sha512-MdazSfcYAUjJjuVTTnusLPzE0pE4VXpOUzWdj8sbM+q6abUjm3bATVPXFqTXrxSieR8ocpvQ9v/QaQCftioQFg==
+jest-mock@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-25.5.0.tgz#a91a54dabd14e37ecd61665d6b6e06360a55387a"
+  integrity sha512-eXWuTV8mKzp/ovHc5+3USJMYsTBhyQ+5A1Mak35dey/RG8GlM4YWVylZuGgVXinaW6tpvk/RSecmF37FKUlpXA==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
+
+jest-mock@^26.0.1:
+  version "26.0.1"
+  resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-26.0.1.tgz#7fd1517ed4955397cf1620a771dc2d61fad8fd40"
+  integrity sha512-MpYTBqycuPYSY6xKJognV7Ja46/TeRbAZept987Zp+tuJvMN0YBWyyhG9mXyYQaU3SBI0TUlSaO5L3p49agw7Q==
+  dependencies:
+    "@jest/types" "^26.0.1"
 
 jest-pnp-resolver@^1.2.1:
   version "1.2.1"
@@ -4162,142 +4071,148 @@ jest-regex-util@^25.2.6:
   resolved "https://registry.yarnpkg.com/jest-regex-util/-/jest-regex-util-25.2.6.tgz#d847d38ba15d2118d3b06390056028d0f2fd3964"
   integrity sha512-KQqf7a0NrtCkYmZZzodPftn7fL1cq3GQAFVMn5Hg8uKx/fIenLEobNanUxb7abQ1sjADHBseG/2FGpsv/wr+Qw==
 
-jest-resolve-dependencies@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.4.0.tgz#783937544cfc40afcc7c569aa54748c4b3f83f5a"
-  integrity sha512-A0eoZXx6kLiuG1Ui7wITQPl04HwjLErKIJTt8GR3c7UoDAtzW84JtCrgrJ6Tkw6c6MwHEyAaLk7dEPml5pf48A==
+jest-resolve-dependencies@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-resolve-dependencies/-/jest-resolve-dependencies-25.5.4.tgz#85501f53957c8e3be446e863a74777b5a17397a7"
+  integrity sha512-yFmbPd+DAQjJQg88HveObcGBA32nqNZ02fjYmtL16t1xw9bAttSn5UGRRhzMHIQbsep7znWvAvnD4kDqOFM0Uw==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     jest-regex-util "^25.2.6"
-    jest-snapshot "^25.4.0"
+    jest-snapshot "^25.5.1"
 
-jest-resolve@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.4.0.tgz#6f4540ce0d419c4c720e791e871da32ba4da7a60"
-  integrity sha512-wOsKqVDFWUiv8BtLMCC6uAJ/pHZkfFgoBTgPtmYlsprAjkxrr2U++ZnB3l5ykBMd2O24lXvf30SMAjJIW6k2aA==
+jest-resolve@^25.5.1:
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/jest-resolve/-/jest-resolve-25.5.1.tgz#0e6fbcfa7c26d2a5fe8f456088dc332a79266829"
+  integrity sha512-Hc09hYch5aWdtejsUZhA+vSzcotf7fajSlPA6EZPE1RmPBAD39XtJhvHWFStid58iit4IPDLI/Da4cwdDmAHiQ==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     browser-resolve "^1.11.3"
     chalk "^3.0.0"
+    graceful-fs "^4.2.4"
     jest-pnp-resolver "^1.2.1"
     read-pkg-up "^7.0.1"
     realpath-native "^2.0.0"
-    resolve "^1.15.1"
+    resolve "^1.17.0"
     slash "^3.0.0"
 
-jest-runner@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.4.0.tgz#6ca4a3d52e692bbc081228fa68f750012f1f29e5"
-  integrity sha512-wWQSbVgj2e/1chFdMRKZdvlmA6p1IPujhpLT7TKNtCSl1B0PGBGvJjCaiBal/twaU2yfk8VKezHWexM8IliBfA==
+jest-runner@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-runner/-/jest-runner-25.5.4.tgz#ffec5df3875da5f5c878ae6d0a17b8e4ecd7c71d"
+  integrity sha512-V/2R7fKZo6blP8E9BL9vJ8aTU4TH2beuqGNxHbxi6t14XzTb+x90B3FRgdvuHm41GY8ch4xxvf0ATH4hdpjTqg==
   dependencies:
-    "@jest/console" "^25.4.0"
-    "@jest/environment" "^25.4.0"
-    "@jest/test-result" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/console" "^25.5.0"
+    "@jest/environment" "^25.5.0"
+    "@jest/test-result" "^25.5.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
     exit "^0.1.2"
-    graceful-fs "^4.2.3"
-    jest-config "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-config "^25.5.4"
     jest-docblock "^25.3.0"
-    jest-haste-map "^25.4.0"
-    jest-jasmine2 "^25.4.0"
-    jest-leak-detector "^25.4.0"
-    jest-message-util "^25.4.0"
-    jest-resolve "^25.4.0"
-    jest-runtime "^25.4.0"
-    jest-util "^25.4.0"
-    jest-worker "^25.4.0"
+    jest-haste-map "^25.5.1"
+    jest-jasmine2 "^25.5.4"
+    jest-leak-detector "^25.5.0"
+    jest-message-util "^25.5.0"
+    jest-resolve "^25.5.1"
+    jest-runtime "^25.5.4"
+    jest-util "^25.5.0"
+    jest-worker "^25.5.0"
     source-map-support "^0.5.6"
     throat "^5.0.0"
 
-jest-runtime@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.4.0.tgz#1e5227a9e2159d26ae27dcd426ca6bc041983439"
-  integrity sha512-lgNJlCDULtXu9FumnwCyWlOub8iytijwsPNa30BKrSNtgoT6NUMXOPrZvsH06U6v0wgD/Igwz13nKA2wEKU2VA==
-  dependencies:
-    "@jest/console" "^25.4.0"
-    "@jest/environment" "^25.4.0"
-    "@jest/source-map" "^25.2.6"
-    "@jest/test-result" "^25.4.0"
-    "@jest/transform" "^25.4.0"
-    "@jest/types" "^25.4.0"
+jest-runtime@^25.5.4:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest-runtime/-/jest-runtime-25.5.4.tgz#dc981fe2cb2137abcd319e74ccae7f7eeffbfaab"
+  integrity sha512-RWTt8LeWh3GvjYtASH2eezkc8AehVoWKK20udV6n3/gC87wlTbE1kIA+opCvNWyyPeBs6ptYsc6nyHUb1GlUVQ==
+  dependencies:
+    "@jest/console" "^25.5.0"
+    "@jest/environment" "^25.5.0"
+    "@jest/globals" "^25.5.2"
+    "@jest/source-map" "^25.5.0"
+    "@jest/test-result" "^25.5.0"
+    "@jest/transform" "^25.5.1"
+    "@jest/types" "^25.5.0"
     "@types/yargs" "^15.0.0"
     chalk "^3.0.0"
     collect-v8-coverage "^1.0.0"
     exit "^0.1.2"
     glob "^7.1.3"
-    graceful-fs "^4.2.3"
-    jest-config "^25.4.0"
-    jest-haste-map "^25.4.0"
-    jest-message-util "^25.4.0"
-    jest-mock "^25.4.0"
+    graceful-fs "^4.2.4"
+    jest-config "^25.5.4"
+    jest-haste-map "^25.5.1"
+    jest-message-util "^25.5.0"
+    jest-mock "^25.5.0"
     jest-regex-util "^25.2.6"
-    jest-resolve "^25.4.0"
-    jest-snapshot "^25.4.0"
-    jest-util "^25.4.0"
-    jest-validate "^25.4.0"
+    jest-resolve "^25.5.1"
+    jest-snapshot "^25.5.1"
+    jest-util "^25.5.0"
+    jest-validate "^25.5.0"
     realpath-native "^2.0.0"
     slash "^3.0.0"
     strip-bom "^4.0.0"
     yargs "^15.3.1"
 
-jest-serializer@^25.2.6:
-  version "25.2.6"
-  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.2.6.tgz#3bb4cc14fe0d8358489dbbefbb8a4e708ce039b7"
-  integrity sha512-RMVCfZsezQS2Ww4kB5HJTMaMJ0asmC0BHlnobQC6yEtxiFKIxohFA4QSXSabKwSggaNkqxn6Z2VwdFCjhUWuiQ==
+jest-serializer@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-serializer/-/jest-serializer-25.5.0.tgz#a993f484e769b4ed54e70e0efdb74007f503072b"
+  integrity sha512-LxD8fY1lByomEPflwur9o4e2a5twSQ7TaVNLlFUuToIdoJuBt8tzHfCsZ42Ok6LkKXWzFWf3AGmheuLAA7LcCA==
+  dependencies:
+    graceful-fs "^4.2.4"
 
-jest-snapshot@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.4.0.tgz#e0b26375e2101413fd2ccb4278a5711b1922545c"
-  integrity sha512-J4CJ0X2SaGheYRZdLz9CRHn9jUknVmlks4UBeu270hPAvdsauFXOhx9SQP2JtRzhnR3cvro/9N9KP83/uvFfRg==
+jest-snapshot@^25.5.1:
+  version "25.5.1"
+  resolved "https://registry.yarnpkg.com/jest-snapshot/-/jest-snapshot-25.5.1.tgz#1a2a576491f9961eb8d00c2e5fd479bc28e5ff7f"
+  integrity sha512-C02JE1TUe64p2v1auUJ2ze5vcuv32tkv9PyhEb318e8XOKF7MOyXdJ7kdjbvrp3ChPLU2usI7Rjxs97Dj5P0uQ==
   dependencies:
     "@babel/types" "^7.0.0"
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     "@types/prettier" "^1.19.0"
     chalk "^3.0.0"
-    expect "^25.4.0"
-    jest-diff "^25.4.0"
+    expect "^25.5.0"
+    graceful-fs "^4.2.4"
+    jest-diff "^25.5.0"
     jest-get-type "^25.2.6"
-    jest-matcher-utils "^25.4.0"
-    jest-message-util "^25.4.0"
-    jest-resolve "^25.4.0"
+    jest-matcher-utils "^25.5.0"
+    jest-message-util "^25.5.0"
+    jest-resolve "^25.5.1"
     make-dir "^3.0.0"
     natural-compare "^1.4.0"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
     semver "^6.3.0"
 
-jest-util@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.4.0.tgz#6a093d09d86d2b41ef583e5fe7dd3976346e1acd"
-  integrity sha512-WSZD59sBtAUjLv1hMeKbNZXmMcrLRWcYqpO8Dz8b4CeCTZpfNQw2q9uwrYAD+BbJoLJlu4ezVPwtAmM/9/SlZA==
+jest-util@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-util/-/jest-util-25.5.0.tgz#31c63b5d6e901274d264a4fec849230aa3fa35b0"
+  integrity sha512-KVlX+WWg1zUTB9ktvhsg2PXZVdkI1NBevOJSkTKYAyXyH4QSvh+Lay/e/v+bmaFfrkfx43xD8QTfgobzlEXdIA==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     chalk "^3.0.0"
+    graceful-fs "^4.2.4"
     is-ci "^2.0.0"
     make-dir "^3.0.0"
 
-jest-validate@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.4.0.tgz#2e177a93b716a137110eaf2768f3d9095abd3f38"
-  integrity sha512-hvjmes/EFVJSoeP1yOl8qR8mAtMR3ToBkZeXrD/ZS9VxRyWDqQ/E1C5ucMTeSmEOGLipvdlyipiGbHJ+R1MQ0g==
+jest-validate@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-validate/-/jest-validate-25.5.0.tgz#fb4c93f332c2e4cf70151a628e58a35e459a413a"
+  integrity sha512-okUFKqhZIpo3jDdtUXUZ2LxGUZJIlfdYBvZb1aczzxrlyMlqdnnws9MOxezoLGhSaFc2XYaHNReNQfj5zPIWyQ==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     camelcase "^5.3.1"
     chalk "^3.0.0"
     jest-get-type "^25.2.6"
     leven "^3.1.0"
-    pretty-format "^25.4.0"
+    pretty-format "^25.5.0"
 
-jest-watcher@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.4.0.tgz#63ec0cd5c83bb9c9d1ac95be7558dd61c995ff05"
-  integrity sha512-36IUfOSRELsKLB7k25j/wutx0aVuHFN6wO94gPNjQtQqFPa2rkOymmx9rM5EzbF3XBZZ2oqD9xbRVoYa2w86gw==
+jest-watcher@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-watcher/-/jest-watcher-25.5.0.tgz#d6110d101df98badebe435003956fd4a465e8456"
+  integrity sha512-XrSfJnVASEl+5+bb51V0Q7WQx65dTSk7NL4yDdVjPnRNpM0hG+ncFmDYJo9O8jaSRcAitVbuVawyXCRoxGrT5Q==
   dependencies:
-    "@jest/test-result" "^25.4.0"
-    "@jest/types" "^25.4.0"
+    "@jest/test-result" "^25.5.0"
+    "@jest/types" "^25.5.0"
     ansi-escapes "^4.2.1"
     chalk "^3.0.0"
-    jest-util "^25.4.0"
+    jest-util "^25.5.0"
     string-length "^3.1.0"
 
 jest-when@~2.7:
@@ -4316,22 +4231,22 @@ jest-worker@^24.9.0:
     merge-stream "^2.0.0"
     supports-color "^6.1.0"
 
-jest-worker@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.4.0.tgz#ee0e2ceee5a36ecddf5172d6d7e0ab00df157384"
-  integrity sha512-ghAs/1FtfYpMmYQ0AHqxV62XPvKdUDIBBApMZfly+E9JEmYh2K45G0R5dWxx986RN12pRCxsViwQVtGl+N4whw==
+jest-worker@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/jest-worker/-/jest-worker-25.5.0.tgz#2611d071b79cea0f43ee57a3d118593ac1547db1"
+  integrity sha512-/dsSmUkIy5EBGfv/IjjqmFxrNAUpBERfGs1oHROyD7yxjG/w+t0GOJDX8O1k32ySmd7+a5IhnJU2qQFcJ4n1vw==
   dependencies:
     merge-stream "^2.0.0"
     supports-color "^7.0.0"
 
-jest@~25.4:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/jest/-/jest-25.4.0.tgz#fb96892c5c4e4a6b9bcb12068849cddf4c5f8cc7"
-  integrity sha512-XWipOheGB4wai5JfCYXd6vwsWNwM/dirjRoZgAa7H2wd8ODWbli2AiKjqG8AYhyx+8+5FBEdpO92VhGlBydzbw==
+jest@~25.5:
+  version "25.5.4"
+  resolved "https://registry.yarnpkg.com/jest/-/jest-25.5.4.tgz#f21107b6489cfe32b076ce2adcadee3587acb9db"
+  integrity sha512-hHFJROBTqZahnO+X+PMtT6G2/ztqAZJveGqz//FnWWHurizkD05PQGzRZOhF3XP6z7SJmL+5tCfW8qV06JypwQ==
   dependencies:
-    "@jest/core" "^25.4.0"
+    "@jest/core" "^25.5.4"
     import-local "^3.0.2"
-    jest-cli "^25.4.0"
+    jest-cli "^25.5.4"
 
 js-tokens@^4.0.0:
   version "4.0.0"
@@ -4645,10 +4560,10 @@ lines-and-columns@^1.1.6:
   resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.1.6.tgz#1c00c743b433cd0a4e80758f7b64a57440d9ff00"
   integrity sha1-HADHQ7QzzQpOgHWPe2SldEDZ/wA=
 
-lint-staged@^10.1.7:
-  version "10.1.7"
-  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.1.7.tgz#b628f8b010083fe4e116d0af7949a32f1ea6b3a7"
-  integrity sha512-ZkK8t9Ep/AHuJQKV95izSa+DqotftGnSsNeEmCSqbQ6j4C4H0jDYhEZqVOGD1Q2Oe227igbqjMWycWyYbQtpoA==
+lint-staged@^10.2.2:
+  version "10.2.2"
+  resolved "https://registry.yarnpkg.com/lint-staged/-/lint-staged-10.2.2.tgz#901403c120eb5d9443a0358b55038b04c8a7db9b"
+  integrity sha512-78kNqNdDeKrnqWsexAmkOU3Z5wi+1CsQmUmfCuYgMTE8E4rAIX8RHW7xgxwAZ+LAayb7Cca4uYX4P3LlevzjVg==
   dependencies:
     chalk "^4.0.0"
     commander "^5.0.0"
@@ -4656,7 +4571,7 @@ lint-staged@^10.1.7:
     debug "^4.1.1"
     dedent "^0.7.0"
     execa "^4.0.0"
-    listr "^0.14.3"
+    listr2 "1.3.8"
     log-symbols "^3.0.0"
     micromatch "^4.0.2"
     normalize-path "^3.0.0"
@@ -4664,49 +4579,25 @@ lint-staged@^10.1.7:
     string-argv "0.3.1"
     stringify-object "^3.3.0"
 
-listr-silent-renderer@^1.1.1:
-  version "1.1.1"
-  resolved "https://registry.yarnpkg.com/listr-silent-renderer/-/listr-silent-renderer-1.1.1.tgz#924b5a3757153770bf1a8e3fbf74b8bbf3f9242e"
-  integrity sha1-kktaN1cVN3C/Go4/v3S4u/P5JC4=
-
-listr-update-renderer@^0.5.0:
-  version "0.5.0"
-  resolved "https://registry.yarnpkg.com/listr-update-renderer/-/listr-update-renderer-0.5.0.tgz#4ea8368548a7b8aecb7e06d8c95cb45ae2ede6a2"
-  integrity sha512-tKRsZpKz8GSGqoI/+caPmfrypiaq+OQCbd+CovEC24uk1h952lVj5sC7SqyFUm+OaJ5HN/a1YLt5cit2FMNsFA==
-  dependencies:
-    chalk "^1.1.3"
-    cli-truncate "^0.2.1"
-    elegant-spinner "^1.0.1"
-    figures "^1.7.0"
-    indent-string "^3.0.0"
-    log-symbols "^1.0.2"
-    log-update "^2.3.0"
-    strip-ansi "^3.0.1"
-
-listr-verbose-renderer@^0.5.0:
-  version "0.5.0"
-  resolved "https://registry.yarnpkg.com/listr-verbose-renderer/-/listr-verbose-renderer-0.5.0.tgz#f1132167535ea4c1261102b9f28dac7cba1e03db"
-  integrity sha512-04PDPqSlsqIOaaaGZ+41vq5FejI9auqTInicFRndCBgE3bXG8D6W1I+mWhk+1nqbHmyhla/6BUrd5OSiHwKRXw==
-  dependencies:
-    chalk "^2.4.1"
-    cli-cursor "^2.1.0"
-    date-fns "^1.27.2"
-    figures "^2.0.0"
-
-listr@^0.14.3:
-  version "0.14.3"
-  resolved "https://registry.yarnpkg.com/listr/-/listr-0.14.3.tgz#2fea909604e434be464c50bddba0d496928fa586"
-  integrity sha512-RmAl7su35BFd/xoMamRjpIE4j3v+L28o8CT5YhAXQJm1fD+1l9ngXY8JAQRJ+tFK2i5njvi0iRUKV09vPwA0iA==
+listr2@1.3.8:
+  version "1.3.8"
+  resolved "https://registry.yarnpkg.com/listr2/-/listr2-1.3.8.tgz#30924d79de1e936d8c40af54b6465cb814a9c828"
+  integrity sha512-iRDRVTgSDz44tBeBBg/35TQz4W+EZBWsDUq7hPpqeUHm7yLPNll0rkwW3lIX9cPAK7l+x95mGWLpxjqxftNfZA==
   dependencies:
     "@samverschueren/stream-to-observable" "^0.3.0"
-    is-observable "^1.1.0"
-    is-promise "^2.1.0"
-    is-stream "^1.1.0"
-    listr-silent-renderer "^1.1.1"
-    listr-update-renderer "^0.5.0"
-    listr-verbose-renderer "^0.5.0"
-    p-map "^2.0.0"
+    chalk "^3.0.0"
+    cli-cursor "^3.1.0"
+    cli-truncate "^2.1.0"
+    elegant-spinner "^2.0.0"
+    enquirer "^2.3.4"
+    figures "^3.2.0"
+    indent-string "^4.0.0"
+    log-update "^4.0.0"
+    p-map "^4.0.0"
+    pad "^3.2.0"
     rxjs "^6.3.3"
+    through "^2.3.8"
+    uuid "^7.0.2"
 
 locate-path@^3.0.0:
   version "3.0.0"
@@ -4748,13 +4639,6 @@ lodash@4.17.15, lodash@^4.17.13, lodash@^4.17.14, lodash@^4.17.15, lodash@^4.2.1
   resolved "https://registry.yarnpkg.com/lodash/-/lodash-4.17.15.tgz#b447f6670a0455bbfeedd11392eff330ea097548"
   integrity sha512-8xOcRHvCjnocdS5cpwXQXVzmmh5e5+saE2QGoeQmbKmRS6J3VQppPOIt0MnmE+4xlZoumy0GPG0D0MVIQbNA1A==
 
-log-symbols@^1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-1.0.2.tgz#376ff7b58ea3086a0f09facc74617eca501e1a18"
-  integrity sha1-N2/3tY6jCGoPCfrMdGF+ylAeGhg=
-  dependencies:
-    chalk "^1.0.0"
-
 log-symbols@^3.0.0:
   version "3.0.0"
   resolved "https://registry.yarnpkg.com/log-symbols/-/log-symbols-3.0.0.tgz#f3a08516a5dea893336a7dee14d18a1cfdab77c4"
@@ -4762,14 +4646,15 @@ log-symbols@^3.0.0:
   dependencies:
     chalk "^2.4.2"
 
-log-update@^2.3.0:
-  version "2.3.0"
-  resolved "https://registry.yarnpkg.com/log-update/-/log-update-2.3.0.tgz#88328fd7d1ce7938b29283746f0b1bc126b24708"
-  integrity sha1-iDKP19HOeTiykoN0bwsbwSayRwg=
+log-update@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/log-update/-/log-update-4.0.0.tgz#589ecd352471f2a1c0c570287543a64dfd20e0a1"
+  integrity sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==
   dependencies:
-    ansi-escapes "^3.0.0"
-    cli-cursor "^2.0.0"
-    wrap-ansi "^3.0.1"
+    ansi-escapes "^4.3.0"
+    cli-cursor "^3.1.0"
+    slice-ansi "^4.0.0"
+    wrap-ansi "^6.2.0"
 
 log4js@^4.0.0:
   version "4.5.1"
@@ -4969,11 +4854,6 @@ mime@^2.3.1:
   resolved "https://registry.yarnpkg.com/mime/-/mime-2.4.4.tgz#bd7b91135fc6b01cde3e9bae33d659b63d8857e5"
   integrity sha512-LRxmNwziLPT828z+4YkNzloCFC2YM4wrB99k+AV5ZbEyfGNWfG8SO1FUXLmLDBSo89NrJZ4DIWeLjy1CHGhMGA==
 
-mimic-fn@^1.0.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-1.2.0.tgz#820c86a39334640e99516928bd03fca88057d022"
-  integrity sha512-jf84uxzwiuiIVKiOLpfYk7N46TSy8ubTonmneY9vrpHNAnp0QBt2BxWV9dO3/j+BoVAb+a5G6YDPW3M5HOdMWQ==
-
 mimic-fn@^2.1.0:
   version "2.1.0"
   resolved "https://registry.yarnpkg.com/mimic-fn/-/mimic-fn-2.1.0.tgz#7ed2c2ccccaf84d3ffcb7a69b57711fc2083401b"
@@ -5205,11 +5085,6 @@ npm-run-path@^4.0.0:
   dependencies:
     path-key "^3.0.0"
 
-number-is-nan@^1.0.0:
-  version "1.0.1"
-  resolved "https://registry.yarnpkg.com/number-is-nan/-/number-is-nan-1.0.1.tgz#097b602b53422a522c1afb8790318336941a011d"
-  integrity sha1-CXtgK1NCKlIsGvuHkDGDNpQaAR0=
-
 nwsapi@^2.2.0:
   version "2.2.0"
   resolved "https://registry.yarnpkg.com/nwsapi/-/nwsapi-2.2.0.tgz#204879a9e3d068ff2a55139c2c772780681a38b7"
@@ -5220,11 +5095,6 @@ oauth-sign@~0.9.0:
   resolved "https://registry.yarnpkg.com/oauth-sign/-/oauth-sign-0.9.0.tgz#47a7b016baa68b5fa0ecf3dee08a85c679ac6455"
   integrity sha512-fexhUFFPTGV8ybAtSIGbV6gOkSv8UtRbDBnAyLQw4QPKkgNlsH2ByPGtMUqdWkos6YCRmAqViwgZrJc/mRDzZQ==
 
-object-assign@^4.1.0:
-  version "4.1.1"
-  resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863"
-  integrity sha1-IQmtx5ZYh8/AXLvUQsrIv7s2CGM=
-
 object-component@0.0.3:
   version "0.0.3"
   resolved "https://registry.yarnpkg.com/object-component/-/object-component-0.0.3.tgz#f0c69aa50efc95b866c186f400a33769cb2f1291"
@@ -5239,17 +5109,12 @@ object-copy@^0.1.0:
     define-property "^0.2.5"
     kind-of "^3.0.3"
 
-object-inspect@^1.7.0:
-  version "1.7.0"
-  resolved "https://registry.yarnpkg.com/object-inspect/-/object-inspect-1.7.0.tgz#f4f6bd181ad77f006b5ece60bd0b6f398ff74a67"
-  integrity sha512-a7pEHdh1xKIAgTySUGgLMx/xwDZskN1Ud6egYYN3EdRW4ZMPNEDUTF+hwy2LUC+Bl+SyLXANnwz/jyh/qutKUw==
-
 object-is@^1.0.1:
   version "1.0.1"
   resolved "https://registry.yarnpkg.com/object-is/-/object-is-1.0.1.tgz#0aa60ec9989a0b3ed795cf4d06f62cf1ad6539b6"
   integrity sha1-CqYOyZiaCz7Xlc9NBvYs8a1lObY=
 
-object-keys@^1.0.11, object-keys@^1.0.12, object-keys@^1.1.1:
+object-keys@^1.0.12:
   version "1.1.1"
   resolved "https://registry.yarnpkg.com/object-keys/-/object-keys-1.1.1.tgz#1c47f272df277f3b1daf061677d9c82e2322c60e"
   integrity sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==
@@ -5268,16 +5133,6 @@ object-visit@^1.0.0:
   dependencies:
     isobject "^3.0.0"
 
-object.assign@^4.1.0:
-  version "4.1.0"
-  resolved "https://registry.yarnpkg.com/object.assign/-/object.assign-4.1.0.tgz#968bf1100d7956bb3ca086f006f846b3bc4008da"
-  integrity sha512-exHJeq6kBKj58mqGyTQ9DFvrZC/eR6OwxzoM9YRoGBqrXYonaFyGiFMuc9VZrXf7DarreEwMpurG3dd+CNyW5w==
-  dependencies:
-    define-properties "^1.1.2"
-    function-bind "^1.1.1"
-    has-symbols "^1.0.0"
-    object-keys "^1.0.11"
-
 object.omit@^1.1.0:
   version "1.1.0"
   resolved "https://registry.yarnpkg.com/object.omit/-/object.omit-1.1.0.tgz#9d17ea16778e5057deba7752c6f55f1496829e94"
@@ -5315,13 +5170,6 @@ once@^1.3.0, once@^1.3.1, once@^1.4.0:
   dependencies:
     wrappy "1"
 
-onetime@^2.0.0:
-  version "2.0.1"
-  resolved "https://registry.yarnpkg.com/onetime/-/onetime-2.0.1.tgz#067428230fd67443b2794b22bba528b6867962d4"
-  integrity sha1-BnQoIw/WdEOyeUsiu6UotoZ5YtQ=
-  dependencies:
-    mimic-fn "^1.0.0"
-
 onetime@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/onetime/-/onetime-5.1.0.tgz#fff0f3c91617fe62bb50189636e99ac8a6df7be5"
@@ -5410,10 +5258,12 @@ p-locate@^4.1.0:
   dependencies:
     p-limit "^2.2.0"
 
-p-map@^2.0.0:
-  version "2.1.0"
-  resolved "https://registry.yarnpkg.com/p-map/-/p-map-2.1.0.tgz#310928feef9c9ecc65b68b17693018a665cea175"
-  integrity sha512-y3b8Kpd8OAN444hxfBbFfj1FY/RjtTd8tzYwhUqNYXx0fXx2iX4maP4Qr6qhIKbQXI02wTLAda4fYUbDagTUFw==
+p-map@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/p-map/-/p-map-4.0.0.tgz#bb2f95a5eda2ec168ec9274e06a747c3e2904d2b"
+  integrity sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==
+  dependencies:
+    aggregate-error "^3.0.0"
 
 p-try@^2.0.0:
   version "2.2.0"
@@ -5705,22 +5555,22 @@ pretty-format@^24.9.0:
     ansi-styles "^3.2.0"
     react-is "^16.8.4"
 
-pretty-format@^25.2.1, pretty-format@^25.3.0:
-  version "25.3.0"
-  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.3.0.tgz#d0a4f988ff4a6cd350342fdabbb809aeb4d49ad5"
-  integrity sha512-wToHwF8bkQknIcFkBqNfKu4+UZqnrLn/Vr+wwKQwwvPzkBfDDKp/qIabFqdgtoi5PEnM8LFByVsOrHoa3SpTVA==
+pretty-format@^25.2.1, pretty-format@^25.4.0:
+  version "25.4.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.4.0.tgz#c58801bb5c4926ff4a677fe43f9b8b99812c7830"
+  integrity sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==
   dependencies:
-    "@jest/types" "^25.3.0"
+    "@jest/types" "^25.4.0"
     ansi-regex "^5.0.0"
     ansi-styles "^4.0.0"
     react-is "^16.12.0"
 
-pretty-format@^25.4.0:
-  version "25.4.0"
-  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.4.0.tgz#c58801bb5c4926ff4a677fe43f9b8b99812c7830"
-  integrity sha512-PI/2dpGjXK5HyXexLPZU/jw5T9Q6S1YVXxxVxco+LIqzUFHXIbKZKdUVt7GcX7QUCr31+3fzhi4gN4/wUYPVxQ==
+pretty-format@^25.5.0:
+  version "25.5.0"
+  resolved "https://registry.yarnpkg.com/pretty-format/-/pretty-format-25.5.0.tgz#7873c1d774f682c34b8d48b6743a2bf2ac55791a"
+  integrity sha512-kbo/kq2LQ/A/is0PQwsEHM7Ca6//bGPPvU6UnsdDRSKTWxT/ru/xb88v4BJf6a69H+uTytOEsTusT9ksd/1iWQ==
   dependencies:
-    "@jest/types" "^25.4.0"
+    "@jest/types" "^25.5.0"
     ansi-regex "^5.0.0"
     ansi-styles "^4.0.0"
     react-is "^16.12.0"
@@ -5745,17 +5595,6 @@ progress@^2.0.3:
   resolved "https://registry.yarnpkg.com/progress/-/progress-2.0.3.tgz#7e8cf8d8f5b8f239c1bc68beb4eb78567d572ef8"
   integrity sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==
 
-promise.allsettled@1.0.2:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/promise.allsettled/-/promise.allsettled-1.0.2.tgz#d66f78fbb600e83e863d893e98b3d4376a9c47c9"
-  integrity sha512-UpcYW5S1RaNKT6pd+s9jp9K9rlQge1UXKskec0j6Mmuq7UJCvlS2J2/s/yuPN8ehftf9HXMxWlKiPbGGUzpoRg==
-  dependencies:
-    array.prototype.map "^1.0.1"
-    define-properties "^1.1.3"
-    es-abstract "^1.17.0-next.1"
-    function-bind "^1.1.1"
-    iterate-value "^1.0.0"
-
 prompts@^2.0.1:
   version "2.2.1"
   resolved "https://registry.yarnpkg.com/prompts/-/prompts-2.2.1.tgz#f901dd2a2dfee080359c0e20059b24188d75ad35"
@@ -6155,12 +5994,19 @@ resolve@1.x, resolve@^1.10.0, resolve@^1.11.0, resolve@^1.14.2, resolve@^1.15.1,
   dependencies:
     path-parse "^1.0.6"
 
-restore-cursor@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-2.0.0.tgz#9f7ee287f82fd326d4fd162923d62129eee0dfaf"
-  integrity sha1-n37ih/gv0ybU/RYpI9YhKe7g368=
+resolve@^1.17.0:
+  version "1.17.0"
+  resolved "https://registry.yarnpkg.com/resolve/-/resolve-1.17.0.tgz#b25941b54968231cc2d1bb76a79cb7f2c0bf8444"
+  integrity sha512-ic+7JYiV8Vi2yzQGFWOkiZD5Z9z7O2Zhm9XMaTxdJExKasieFCr+yXZ/WmXsckHiKl12ar0y6XiXDx3m4RHn1w==
   dependencies:
-    onetime "^2.0.0"
+    path-parse "^1.0.6"
+
+restore-cursor@^3.1.0:
+  version "3.1.0"
+  resolved "https://registry.yarnpkg.com/restore-cursor/-/restore-cursor-3.1.0.tgz#39f67c54b3a7a58cea5236d95cf0034239631f7e"
+  integrity sha512-l+sSefzHpj5qimhFSE5a8nufZYAM3sBSVMAPtYkmC+4EH2anSGaEMXSD0izRQbu9nfyQ9y5JrVmp7E8oZrUjvA==
+  dependencies:
+    onetime "^5.1.0"
     signal-exit "^3.0.2"
 
 ret@~0.1.10:
@@ -6236,10 +6082,10 @@ rollup-pluginutils@^2.8.2:
   dependencies:
     estree-walker "^0.6.1"
 
-rollup@^2.7.2:
-  version "2.7.2"
-  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.7.2.tgz#0f8ee6216d33e83c0750116312c2c92b94cc7f72"
-  integrity sha512-SdtTZVMMVSPe7SNv4exUyPXARe5v/p3TeeG3LRA5WabLPJt4Usi3wVrvVlyAUTG40JJmqS6zbIHt2vWTss2prw==
+rollup@^2.10.0:
+  version "2.10.0"
+  resolved "https://registry.yarnpkg.com/rollup/-/rollup-2.10.0.tgz#73332273fa177cd85c1042659dee6f103761be0d"
+  integrity sha512-7BmpEfUN9P6esJzWIn3DmR//90mW6YwYB1t3y48LpF8ITpYtL8s1kEirMKqUu44dVH/6a/rs0EuwYVL3FuRDoA==
   optionalDependencies:
     fsevents "~2.1.2"
 
@@ -6434,10 +6280,23 @@ slash@^3.0.0:
   resolved "https://registry.yarnpkg.com/slash/-/slash-3.0.0.tgz#6539be870c165adbd5240220dbe361f1bc4d4634"
   integrity sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==
 
-slice-ansi@0.0.4:
-  version "0.0.4"
-  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-0.0.4.tgz#edbf8903f66f7ce2f8eafd6ceed65e264c831b35"
-  integrity sha1-7b+JA/ZvfOL46v1s7tZeJkyDGzU=
+slice-ansi@^3.0.0:
+  version "3.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-3.0.0.tgz#31ddc10930a1b7e0b67b08c96c2f49b77a789787"
+  integrity sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
+
+slice-ansi@^4.0.0:
+  version "4.0.0"
+  resolved "https://registry.yarnpkg.com/slice-ansi/-/slice-ansi-4.0.0.tgz#500e8dd0fd55b05815086255b3195adf2a45fe6b"
+  integrity sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==
+  dependencies:
+    ansi-styles "^4.0.0"
+    astral-regex "^2.0.0"
+    is-fullwidth-code-point "^3.0.0"
 
 smart-buffer@^4.1.0:
   version "4.1.0"
@@ -6719,23 +6578,6 @@ string-length@^3.1.0:
     astral-regex "^1.0.0"
     strip-ansi "^5.2.0"
 
-string-width@^1.0.1:
-  version "1.0.2"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-1.0.2.tgz#118bdf5b8cdc51a2a7e70d211e07e2b0b9b107d3"
-  integrity sha1-EYvfW4zcUaKn5w0hHgfisLmxB9M=
-  dependencies:
-    code-point-at "^1.0.0"
-    is-fullwidth-code-point "^1.0.0"
-    strip-ansi "^3.0.0"
-
-string-width@^2.1.1:
-  version "2.1.1"
-  resolved "https://registry.yarnpkg.com/string-width/-/string-width-2.1.1.tgz#ab93f27a8dc13d28cac815c462143a6d9012ae9e"
-  integrity sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==
-  dependencies:
-    is-fullwidth-code-point "^2.0.0"
-    strip-ansi "^4.0.0"
-
 string-width@^3.0.0, string-width@^3.1.0:
   version "3.1.0"
   resolved "https://registry.yarnpkg.com/string-width/-/string-width-3.1.0.tgz#22767be21b62af1081574306f69ac51b62203961"
@@ -6754,40 +6596,6 @@ string-width@^4.1.0, string-width@^4.2.0:
     is-fullwidth-code-point "^3.0.0"
     strip-ansi "^6.0.0"
 
-string.prototype.trimend@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimend/-/string.prototype.trimend-1.0.0.tgz#ee497fd29768646d84be2c9b819e292439614373"
-  integrity sha512-EEJnGqa/xNfIg05SxiPSqRS7S9qwDhYts1TSLR1BQfYUfPe1stofgGKvwERK9+9yf+PpfBMlpBaCHucXGPQfUA==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.5"
-
-string.prototype.trimleft@^2.1.1:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimleft/-/string.prototype.trimleft-2.1.2.tgz#4408aa2e5d6ddd0c9a80739b087fbc067c03b3cc"
-  integrity sha512-gCA0tza1JBvqr3bfAIFJGqfdRTyPae82+KTnm3coDXkZN9wnuW3HjGgN386D7hfv5CHQYCI022/rJPVlqXyHSw==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.5"
-    string.prototype.trimstart "^1.0.0"
-
-string.prototype.trimright@^2.1.1:
-  version "2.1.2"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimright/-/string.prototype.trimright-2.1.2.tgz#c76f1cef30f21bbad8afeb8db1511496cfb0f2a3"
-  integrity sha512-ZNRQ7sY3KroTaYjRS6EbNiiHrOkjihL9aQE/8gfQ4DtAC/aEBRHFJa44OmoWxGGqXuJlfKkZW4WcXErGr+9ZFg==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.5"
-    string.prototype.trimend "^1.0.0"
-
-string.prototype.trimstart@^1.0.0:
-  version "1.0.0"
-  resolved "https://registry.yarnpkg.com/string.prototype.trimstart/-/string.prototype.trimstart-1.0.0.tgz#afe596a7ce9de905496919406c9734845f01a2f2"
-  integrity sha512-iCP8g01NFYiiBOnwG1Xc3WZLyoo+RuBymwIlWncShXDDJYWN6DbnM3odslBJdgCdRlq94B5s63NWAZlcn2CS4w==
-  dependencies:
-    define-properties "^1.1.3"
-    es-abstract "^1.17.5"
-
 string_decoder@^1.1.1, string_decoder@^1.3.0:
   version "1.3.0"
   resolved "https://registry.yarnpkg.com/string_decoder/-/string_decoder-1.3.0.tgz#42f114594a46cf1a8e30b0a84f56c78c3edac21e"
@@ -6823,20 +6631,6 @@ strip-ansi@6.0, strip-ansi@^6.0.0:
   dependencies:
     ansi-regex "^5.0.0"
 
-strip-ansi@^3.0.0, strip-ansi@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-3.0.1.tgz#6a385fb8853d952d5ff05d0e8aaf94278dc63dcf"
-  integrity sha1-ajhfuIU9lS1f8F0Oiq+UJ43GPc8=
-  dependencies:
-    ansi-regex "^2.0.0"
-
-strip-ansi@^4.0.0:
-  version "4.0.0"
-  resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-4.0.0.tgz#a8479022eb1ac368a871389b635262c505ee368f"
-  integrity sha1-qEeQIusaw2iocTibY1JixQXuNo8=
-  dependencies:
-    ansi-regex "^3.0.0"
-
 strip-ansi@^5.0.0, strip-ansi@^5.1.0, strip-ansi@^5.2.0:
   version "5.2.0"
   resolved "https://registry.yarnpkg.com/strip-ansi/-/strip-ansi-5.2.0.tgz#8c9a536feb6afc962bdfa5b104a5091c1ad9c0ae"
@@ -6864,11 +6658,6 @@ strip-final-newline@^2.0.0:
   resolved "https://registry.yarnpkg.com/strip-final-newline/-/strip-final-newline-2.0.0.tgz#89b852fb2fcbe936f6f4b3187afb0a12c1ab58ad"
   integrity sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==
 
-supports-color@^2.0.0:
-  version "2.0.0"
-  resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-2.0.0.tgz#535d045ce6b6363fa40117084629995e9df324c7"
-  integrity sha1-U10EXOa2Nj+kARcIRimZXp3zJMc=
-
 supports-color@^5.3.0:
   version "5.5.0"
   resolved "https://registry.yarnpkg.com/supports-color/-/supports-color-5.5.0.tgz#e2e69a44ac8772f78a1ec0b35b689df6530efc8f"
@@ -6898,11 +6687,6 @@ supports-hyperlinks@^2.0.0:
     has-flag "^4.0.0"
     supports-color "^7.0.0"
 
-symbol-observable@^1.1.0:
-  version "1.2.0"
-  resolved "https://registry.yarnpkg.com/symbol-observable/-/symbol-observable-1.2.0.tgz#c22688aed4eab3cdc2dfeacbb561660560a00804"
-  integrity sha512-e900nM8RRtGhlV36KGEU9k65K3mPb1WV70OdjfxlG2EAuM1noi/E/BaW/uMhL7bPEssK8QV57vN3esixjUvcXQ==
-
 symbol-tree@^3.2.2:
   version "3.2.4"
   resolved "https://registry.yarnpkg.com/symbol-tree/-/symbol-tree-3.2.4.tgz#430637d248ba77e078883951fb9aa0eed7c63fa2"
@@ -6957,6 +6741,11 @@ through2@^2.0.0, through2@^2.0.1:
     readable-stream "~2.3.6"
     xtend "~4.0.1"
 
+through@^2.3.8:
+  version "2.3.8"
+  resolved "https://registry.yarnpkg.com/through/-/through-2.3.8.tgz#0dd4c9ffaabc357960b1b724115d7e0e86a2e1f5"
+  integrity sha1-DdTJ/6q8NXlgsbckEV1+Doai4fU=
+
 thunkify@^2.1.2:
   version "2.1.2"
   resolved "https://registry.yarnpkg.com/thunkify/-/thunkify-2.1.2.tgz#faa0e9d230c51acc95ca13a361ac05ca7e04553d"
@@ -7075,10 +6864,10 @@ ts-jest@^25.0.0:
     semver "^5.5"
     yargs-parser "10.x"
 
-ts-node@^8.9.1:
-  version "8.9.1"
-  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.9.1.tgz#2f857f46c47e91dcd28a14e052482eb14cfd65a5"
-  integrity sha512-yrq6ODsxEFTLz0R3BX2myf0WBCSQh9A+py8PBo1dCzWIOcvisbyH6akNKqDHMgXePF2kir5mm5JXJTH3OUJYOQ==
+ts-node@^8.10.1:
+  version "8.10.1"
+  resolved "https://registry.yarnpkg.com/ts-node/-/ts-node-8.10.1.tgz#77da0366ff8afbe733596361d2df9a60fc9c9bd3"
+  integrity sha512-bdNz1L4ekHiJul6SHtZWs1ujEKERJnHs4HxN7rjTyyVOFf3HaJ6sLqe6aPG62XTzAB/63pKRh5jTSWL0D7bsvw==
   dependencies:
     arg "^4.1.0"
     diff "^4.0.1"
@@ -7180,6 +6969,11 @@ type-detect@4.0.8:
   resolved "https://registry.yarnpkg.com/type-detect/-/type-detect-4.0.8.tgz#7646fb5f18871cfbb7749e69bd39a6388eb7450c"
   integrity sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==
 
+type-fest@^0.11.0:
+  version "0.11.0"
+  resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.11.0.tgz#97abf0872310fed88a5c466b25681576145e33f1"
+  integrity sha512-OdjXJxnCN1AvyLSzeKIgXTXxV+99ZuXl3Hpo9XpJAv9MBcHrrJOQ5kV7ypXOuQie+AmWG25hLbiKdwYTifzcfQ==
+
 type-fest@^0.6.0:
   version "0.6.0"
   resolved "https://registry.yarnpkg.com/type-fest/-/type-fest-0.6.0.tgz#8d2a2370d3df886eb5c90ada1c5bf6188acf838b"
@@ -7341,6 +7135,11 @@ uuid@^3.3.2:
   resolved "https://registry.yarnpkg.com/uuid/-/uuid-3.3.3.tgz#4568f0216e78760ee1dbf3a4d2cf53e224112866"
   integrity sha512-pW0No1RGHgzlpHJO1nsVrHKpOEIxkGg1xB+v0ZmdNH5OAeAwzAVrCnI2/6Mtx+Uys6iaylxa+D3g4j63IKKjSQ==
 
+uuid@^7.0.2:
+  version "7.0.3"
+  resolved "https://registry.yarnpkg.com/uuid/-/uuid-7.0.3.tgz#c5c9f2c8cf25dc0a372c4df1441c41f5bd0c680b"
+  integrity sha512-DPSke0pXhTZgoF/d+WSt2QaKMCFSfx7QegxEWT+JOuHF5aWrKEn0G+ztjuJg/gG8/ItK+rbPCD/yNv8yyih6Cg==
+
 v8-to-istanbul@^4.1.3:
   version "4.1.3"
   resolved "https://registry.yarnpkg.com/v8-to-istanbul/-/v8-to-istanbul-4.1.3.tgz#22fe35709a64955f49a08a7c7c959f6520ad6f20"
@@ -7476,14 +7275,6 @@ wordwrap@~0.0.2:
   resolved "https://registry.yarnpkg.com/wordwrap/-/wordwrap-0.0.3.tgz#a3d5da6cd5c0bc0008d37234bbaf1bed63059107"
   integrity sha1-o9XabNXAvAAI03I0u68b7WMFkQc=
 
-wrap-ansi@^3.0.1:
-  version "3.0.1"
-  resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-3.0.1.tgz#288a04d87eda5c286e060dfe8f135ce8d007f8ba"
-  integrity sha1-KIoE2H7aXChuBg3+jxNc6NAH+Lo=
-  dependencies:
-    string-width "^2.1.1"
-    strip-ansi "^4.0.0"
-
 wrap-ansi@^5.1.0:
   version "5.1.0"
   resolved "https://registry.yarnpkg.com/wrap-ansi/-/wrap-ansi-5.1.0.tgz#1fd1f67235d5b6d0fee781056001bfb694c03b09"


Test patch:
diff --git a/setupTests.ts b/setupTests.ts
index e01b4134..b5b133ba 100644
--- a/setupTests.ts
+++ b/setupTests.ts
@@ -1,7 +1,6 @@
 import { RulesetExceptionCollection } from './src/types/ruleset';
 
-import { Dictionary } from '@stoplight/types';
-import { IRule, IRunRule, isAsyncApiv2, Spectral } from './src';
+import { IRule, isAsyncApiv2, Rule, RuleCollection, Spectral } from './src';
 import { rules as asyncApiRules } from './src/rulesets/asyncapi/index.json';
 
 export const buildRulesetExceptionCollectionFrom = (
@@ -20,17 +19,17 @@ const removeAllRulesBut = (spectral: Spectral, ruleName: string) => {
 
   const rawRule = asyncApiRules[ruleName];
 
-  const patchedRule: IRule = Object.assign(rule1, {
+  const patchedRule = Object.assign(rule1, {
     recommended: true,
     severity: rawRule.severity,
-  });
+  }) as IRule;
 
-  const rules: Dictionary<IRule, string> = {};
+  const rules: RuleCollection = {};
   rules[ruleName] = patchedRule;
   spectral.setRules(rules);
 };
 
-export const buildTestSpectralWithAsyncApiRule = async (ruleName: string): Promise<[Spectral, IRunRule]> => {
+export const buildTestSpectralWithAsyncApiRule = async (ruleName: string): Promise<[Spectral, Rule]> => {
   const s = new Spectral();
   s.registerFormat('asyncapi2', isAsyncApiv2);
   await s.loadRuleset('spectral:asyncapi');
@@ -39,7 +38,7 @@ export const buildTestSpectralWithAsyncApiRule = async (ruleName: string): Promi
   expect(Object.keys(s.rules)).toEqual([ruleName]);
 
   const rule = s.rules[ruleName];
-  expect(rule.recommended).not.toBe(false);
+  expect(rule.enabled).not.toBe(false);
   expect(rule.severity).not.toBeUndefined();
   expect(rule.severity).not.toEqual(-1);
   expect(rule.formats).not.toBeUndefined();
diff --git a/src/__tests__/__fixtures__/bare-oas-ruleset.json b/src/__tests__/__fixtures__/bare-oas-ruleset.json
index 3c980184..a4983421 100644
--- a/src/__tests__/__fixtures__/bare-oas-ruleset.json
+++ b/src/__tests__/__fixtures__/bare-oas-ruleset.json
@@ -13,5 +13,12 @@
         }
       }
     }
+  },
+  "except": {
+    "/test/file.json#/info": ["info-contact", "info-description"],
+    "/test/file.json#": [ "oas3-api-servers"],
+    "/test/file.json#/paths/~1a.two/get": ["operation-2xx-response"],
+    "/test/file.json#/paths/~1b.three/get": ["operation-2xx-response"],
+    "another.yaml#": ["dummy-rule", "info-contact"]
   }
 }
diff --git a/src/__tests__/functions.test.ts b/src/__tests__/functions.test.ts
index 4bd7fe97..0b1fb595 100644
--- a/src/__tests__/functions.test.ts
+++ b/src/__tests__/functions.test.ts
@@ -1,4 +1,5 @@
-import { IRule, Spectral } from '../spectral';
+import { IRule } from '..';
+import { Spectral } from '../spectral';
 import { IRuleResult } from '../types';
 
 const applyRuleToObject = async (r: IRule, o: object): Promise<IRuleResult[]> => {
diff --git a/src/__tests__/linter.jest.test.ts b/src/__tests__/linter.jest.test.ts
index 3be14228..7d4a779c 100644
--- a/src/__tests__/linter.jest.test.ts
+++ b/src/__tests__/linter.jest.test.ts
@@ -10,7 +10,7 @@ import { httpAndFileResolver } from '../resolvers/http-and-file';
 import { readRuleset } from '../rulesets';
 import { setFunctionContext } from '../rulesets/evaluators';
 import oasDocumentSchema from '../rulesets/oas/functions/oasDocumentSchema';
-import { Spectral } from '../spectral';
+import { IFunctionResult, Spectral } from '../spectral';
 import { IRuleset, RulesetExceptionCollection } from '../types/ruleset';
 
 const customFunctionOASRuleset = path.join(__dirname, './__fixtures__/custom-functions-oas-ruleset.json');
@@ -257,7 +257,7 @@ console.log(this.cache.get('test') || this.cache.set('test', []).get('test'));
       it('should handle basic example', async () => {
         spectral.setFunctions({
           [fnName]() {
-            return new Promise(resolve => {
+            return new Promise<IFunctionResult[]>(resolve => {
               setTimeout(resolve, 200, [
                 {
                   message: 'Error reported by async fn',
@@ -289,7 +289,7 @@ console.log(this.cache.get('test') || this.cache.set('test', []).get('test'));
       it('should handle rejections', async () => {
         spectral.setFunctions({
           [fnName]() {
-            return new Promise((resolve, reject) => {
+            return new Promise<void>((resolve, reject) => {
               setTimeout(reject, 1000, new Error('Some unknown error'));
             });
           },
@@ -735,7 +735,7 @@ console.log(this.cache.get('test') || this.cache.set('test', []).get('test'));
 
     expect(Object.keys(spectral.rules)).toHaveLength(3);
 
-    expect(Object.entries(spectral.rules).map(([name, rule]) => [name, rule.recommended])).toEqual([
+    expect(Object.entries(spectral.rules).map(([name, rule]) => [name, rule.enabled])).toEqual([
       ['explicitly-recommended', true],
       ['implicitly-recommended', true],
       ['explicitly-not-recommended', false],
diff --git a/src/__tests__/linter.test.ts b/src/__tests__/linter.test.ts
index 0e215338..571280d9 100644
--- a/src/__tests__/linter.test.ts
+++ b/src/__tests__/linter.test.ts
@@ -1,6 +1,7 @@
 import { Resolver } from '@stoplight/json-ref-resolver';
 import { DiagnosticSeverity } from '@stoplight/types';
 import { parse } from '@stoplight/yaml';
+import { omit } from 'lodash';
 import { IParsedResult } from '../document';
 import { isOpenApiv2, isOpenApiv3 } from '../formats';
 import { mergeRules, readRuleset } from '../rulesets';
@@ -59,7 +60,7 @@ describe('linter', () => {
     const message = '4xx responses require a description';
 
     spectral.setFunctions({
-      func1: val => {
+      func1: (val: unknown) => {
         if (!val) {
           return [
             {
@@ -150,16 +151,20 @@ describe('linter', () => {
   test('should not report anything for disabled rules', async () => {
     await spectral.loadRuleset('spectral:oas');
     const { rules: oasRules } = await readRuleset('spectral:oas');
-    spectral.setRules(
-      mergeRules(oasRules, {
+    spectral.setRules({
+      ...mergeRules(oasRules, {
         'oas3-valid-schema-example': 'off',
         'operation-2xx-response': -1,
         'openapi-tags': 'off',
-      }) as RuleCollection,
-    );
-
-    // @ts-ignore
-    spectral.rules['oas3-schema'].then.function = 'oasDocumentSchema';
+        'operation-tag-defined': 'off',
+      }),
+      ...omit(spectral.rules, [
+        'oas3-valid-schema-example',
+        'operation-2xx-response',
+        'openapi-tags',
+        'operation-tag-defined',
+      ]),
+    } as RuleCollection);
 
     const result = await spectral.run(invalidSchema);
 
@@ -530,9 +535,7 @@ describe('linter', () => {
   });
 
   test('should include parser diagnostics', async () => {
-    await spectral.loadRuleset('spectral:oas');
-
-    const responses = `openapi: 2.0.0
+    const responses = `
 responses:: !!foo
   400:
     description: a
@@ -542,7 +545,7 @@ responses:: !!foo
      description: c
 `;
 
-    const result = await spectral.run(responses);
+    const result = await spectral.run(responses, { ignoreUnknownFormat: true });
 
     expect(result).toEqual(
       expect.arrayContaining([
@@ -671,26 +674,22 @@ responses:: !!foo
   });
 
   test('should report invalid $refs', async () => {
-    await spectral.loadRuleset('spectral:oas');
-
     const result = await spectral.run(invalidSchema);
 
-    expect(result).toEqual(
-      expect.arrayContaining([
-        expect.objectContaining({
-          code: 'invalid-ref',
-          message: "No resolver defined for scheme 'file' in ref ./models/pet.yaml",
-          path: ['paths', '/pets', 'get', 'responses', '200', 'content', 'application/json', 'schema', '$ref'],
-          severity: DiagnosticSeverity.Error,
-        }),
-        expect.objectContaining({
-          code: 'invalid-ref',
-          message: "No resolver defined for scheme 'file' in ref ../common/models/error.yaml",
-          path: ['paths', '/pets', 'get', 'responses', 'default', 'content', 'application/json', 'schema', '$ref'],
-          severity: DiagnosticSeverity.Error,
-        }),
-      ]),
-    );
+    expect(result).toEqual([
+      expect.objectContaining({
+        code: 'invalid-ref',
+        message: "No resolver defined for scheme 'file' in ref ./models/pet.yaml",
+        path: ['paths', '/pets', 'get', 'responses', '200', 'content', 'application/json', 'schema', '$ref'],
+        severity: DiagnosticSeverity.Error,
+      }),
+      expect.objectContaining({
+        code: 'invalid-ref',
+        message: "No resolver defined for scheme 'file' in ref ../common/models/error.yaml",
+        path: ['paths', '/pets', 'get', 'responses', 'default', 'content', 'application/json', 'schema', '$ref'],
+        severity: DiagnosticSeverity.Error,
+      }),
+    ]);
   });
 
   test('should support YAML merge keys', async () => {
@@ -698,6 +697,8 @@ responses:: !!foo
     spectral.setRules({
       'operation-tag-defined': {
         ...spectral.rules['operation-tag-defined'],
+        message: spectral.rules['operation-tag-defined'].message ?? '',
+        description: spectral.rules['operation-tag-defined'].description ?? '',
         severity: 'off',
       },
     });
@@ -1024,6 +1025,7 @@ responses:: !!foo
     spectral.setRules({
       'oas3-schema': {
         ...spectral.rules['oas3-schema'],
+        description: spectral.rules['oas3-schema'].description ?? '',
         message: 'Schema error at {{path}}',
       },
     });
diff --git a/src/__tests__/spectral.jest.test.ts b/src/__tests__/spectral.jest.test.ts
index 6cb2ab2d..e5dbeb2c 100644
--- a/src/__tests__/spectral.jest.test.ts
+++ b/src/__tests__/spectral.jest.test.ts
@@ -1,5 +1,5 @@
 import { normalize } from '@stoplight/path';
-import { DiagnosticSeverity, Dictionary } from '@stoplight/types';
+import { DiagnosticSeverity } from '@stoplight/types';
 import * as fs from 'fs';
 import * as nock from 'nock';
 import * as path from 'path';
@@ -9,11 +9,9 @@ import { isOpenApiv2 } from '../formats';
 import { pattern } from '../functions/pattern';
 import * as Parsers from '../parsers';
 import { httpAndFileResolver } from '../resolvers/http-and-file';
-import { IRunRule, Spectral } from '../spectral';
+import { Spectral } from '../spectral';
 
-const oasRuleset = require('../rulesets/oas/index.json');
-const oasRulesetRules: Dictionary<IRunRule, string> = oasRuleset.rules;
-const customOASRuleset = require('./__fixtures__/custom-oas-ruleset.json');
+const bareRuleset = require('./__fixtures__/bare-oas-ruleset.json');
 
 describe('Spectral', () => {
   afterEach(() => {
@@ -23,29 +21,16 @@ describe('Spectral', () => {
   describe('loadRuleset', () => {
     test('should support loading rulesets from filesystem', async () => {
       const s = new Spectral();
-      await s.loadRuleset(path.join(__dirname, '__fixtures__/custom-oas-ruleset.json'));
+      await s.loadRuleset(path.join(__dirname, '__fixtures__/bare-oas-ruleset.json'));
 
-      expect(s.rules).toEqual(
-        expect.objectContaining({
-          ...[...Object.entries(oasRulesetRules)].reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
-            oasRules[name] = {
-              name,
-              ...rule,
-              formats: expect.arrayContaining([expect.any(String)]),
-              recommended: expect.any(Boolean),
-              severity: expect.any(Number),
-              then: expect.any(Object),
-            };
-
-            return oasRules;
-          }, {}),
-          'info-matches-stoplight': {
-            ...customOASRuleset.rules['info-matches-stoplight'],
-            name: 'info-matches-stoplight',
-            severity: DiagnosticSeverity.Warning,
-          },
+      expect(s.rules).toEqual({
+        'info-matches-stoplight': expect.objectContaining({
+          message: bareRuleset.rules['info-matches-stoplight'].message,
+          name: 'info-matches-stoplight',
+          given: [bareRuleset.rules['info-matches-stoplight'].given],
+          severity: DiagnosticSeverity.Warning,
         }),
-      );
+      });
 
       Object.keys(s.exceptions).forEach(p => expect(path.isAbsolute(p)).toEqual(true));
 
@@ -84,12 +69,12 @@ describe('Spectral', () => {
       await s.loadRuleset('https://localhost:4000/custom-ruleset');
 
       expect(s.rules).toEqual({
-        'info-matches-stoplight': {
-          ...ruleset.rules['info-matches-stoplight'],
+        'info-matches-stoplight': expect.objectContaining({
+          message: bareRuleset.rules['info-matches-stoplight'].message,
           name: 'info-matches-stoplight',
-          recommended: true,
+          given: [bareRuleset.rules['info-matches-stoplight'].given],
           severity: DiagnosticSeverity.Warning,
-        },
+        }),
       });
     });
   });
diff --git a/src/__tests__/spectral.karma.test.ts b/src/__tests__/spectral.karma.test.ts
index b446f714..48f20584 100644
--- a/src/__tests__/spectral.karma.test.ts
+++ b/src/__tests__/spectral.karma.test.ts
@@ -42,12 +42,13 @@ describe('Spectral', () => {
       await s.loadRuleset('https://localhost:4000/custom-ruleset');
 
       expect(s.rules).toEqual({
-        'info-matches-stoplight': {
-          ...ruleset.rules['info-matches-stoplight'],
+        'info-matches-stoplight': expect.objectContaining({
+          given: ['$.info'],
           name: 'info-matches-stoplight',
-          recommended: true,
+          message: 'Info must contain Stoplight',
+          enabled: true,
           severity: DiagnosticSeverity.Warning,
-        },
+        }),
       });
     });
   });
diff --git a/src/__tests__/spectral.test.ts b/src/__tests__/spectral.test.ts
index 20c8fbd8..51325510 100644
--- a/src/__tests__/spectral.test.ts
+++ b/src/__tests__/spectral.test.ts
@@ -1,20 +1,19 @@
 import { IGraphNodeData } from '@stoplight/json-ref-resolver/types';
-import { DiagnosticSeverity, Dictionary } from '@stoplight/types';
+import { DiagnosticSeverity } from '@stoplight/types';
 import { DepGraph } from 'dependency-graph';
 import { escapeRegExp, merge } from 'lodash';
 
+import { buildRulesetExceptionCollectionFrom } from '../../setupTests';
 import { Document } from '../document';
 import * as Parsers from '../parsers';
-import { Spectral } from '../spectral';
-import { IResolver, IRunRule } from '../types';
+import { RunRuleCollection, Spectral } from '../spectral';
+import { IResolver, RuleCollection } from '../types';
 import { RulesetExceptionCollection } from '../types/ruleset';
 
-import { buildRulesetExceptionCollectionFrom } from '../../setupTests';
-
 const oasRuleset = JSON.parse(JSON.stringify(require('../rulesets/oas/index.json')));
 const asyncApiRuleset = JSON.parse(JSON.stringify(require('../rulesets/asyncapi/index.json')));
-const oasRulesetRules: Dictionary<IRunRule, string> = oasRuleset.rules;
-const asyncApiRulesetRules: Dictionary<IRunRule, string> = asyncApiRuleset.rules;
+const oasRulesetRules: RuleCollection = oasRuleset.rules;
+const asyncApiRulesetRules: RuleCollection = asyncApiRuleset.rules;
 
 describe('spectral', () => {
   describe('loadRuleset', () => {
@@ -27,15 +26,17 @@ describe('spectral', () => {
 
       expect(s.rules).toEqual(
         expect.objectContaining(
-          Object.entries(rules).reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
-            oasRules[name] = {
+          Object.entries(rules).reduce<RunRuleCollection>((oasRules, [name, rule]) => {
+            oasRules[name] = expect.objectContaining({
               name,
-              ...rule,
+              given: expect.anything(),
               formats: expect.arrayContaining([expect.any(String)]),
-              recommended: expect.any(Boolean),
+              enabled: expect.any(Boolean),
               severity: expect.any(Number),
-              then: expect.any(Object),
-            };
+              then: expect.any(Array),
+              message: rule.message ?? null,
+              description: rule.description ?? null,
+            });
 
             return oasRules;
           }, {}),
@@ -51,15 +52,17 @@ describe('spectral', () => {
       await s.loadRuleset([rulesetName, rulesetName]);
 
       expect(s.rules).toEqual(
-        Object.entries(expectedRules).reduce<Dictionary<IRunRule, string>>((oasRules, [name, rule]) => {
-          oasRules[name] = {
+        Object.entries(expectedRules).reduce<RunRuleCollection>((oasRules, [name, rule]) => {
+          oasRules[name] = expect.objectContaining({
             name,
-            ...rule,
+            given: expect.anything(),
             formats: expect.arrayContaining([expect.any(String)]),
-            recommended: expect.any(Boolean),
+            enabled: expect.any(Boolean),
             severity: expect.any(Number),
-            then: expect.any(Object),
-          };
+            then: expect.any(Array),
+            message: rule.message ?? null,
+            description: rule.description ?? null,
+          });
 
           return oasRules;
         }, {}),
diff --git a/src/functions/__tests__/casing.test.ts b/src/functions/__tests__/casing.test.ts
index dd6e32de..b5a774f1 100644
--- a/src/functions/__tests__/casing.test.ts
+++ b/src/functions/__tests__/casing.test.ts
@@ -1,4 +1,3 @@
-import { AssertionError } from 'assert';
 import { casing, CasingType } from '../casing';
 
 function runCasing(target: unknown, type: CasingType, disallowDigits?: boolean, separator?: any) {
@@ -20,10 +19,6 @@ describe('casing', () => {
     expect(runCasing('', CasingType.camel)).toBeUndefined();
   });
 
-  test('given unknown case type should return nothing', () => {
-    expect(() => runCasing('2', 'foo' as any)).toThrowError(AssertionError);
-  });
-
   describe('casing type', () => {
     describe('flat', () => {
       const invalid = ['foo_test', 'Foo', '123', '1d', 'foo-bar'];
@@ -220,39 +215,6 @@ describe('casing', () => {
   });
 
   describe('casing with custom separator configuration', () => {
-    describe('parameters validation', () => {
-      const casingTypes = Object.keys(CasingType);
-
-      test.each([...casingTypes])(
-        'with "%s" type - throws when allowLeadingSeparator option is used without specifying separator',
-        type => {
-          expect(() =>
-            runCasing('irrelevant value', CasingType[type], undefined, { char: undefined, allowLeading: true }),
-          ).toThrow(AssertionError);
-        },
-      );
-
-      const invalidSepLengthCombinations = Object.values(casingTypes).reduce<Array<[string, number]>>(
-        (combinations, type) => {
-          for (const len of [0, 2, 3, 17]) {
-            combinations.push([type, len]);
-          }
-
-          return combinations;
-        },
-        [],
-      );
-
-      test.each(invalidSepLengthCombinations)(
-        'with type "%s" - throws when separator value is "%i" (not exactly one char long)',
-        (type, len) => {
-          expect(() => runCasing('irrelevant value', CasingType[type], undefined, { char: '-'.repeat(len) })).toThrow(
-            AssertionError,
-          );
-        },
-      );
-    });
-
     const baseData: Array<[string, string, string, string]> = [[CasingType.flat, 'flat', 'flat01', 'Nope']];
 
     const testCases: Array<[string, boolean, string, boolean, string, string, string]> = [];
diff --git a/src/functions/__tests__/schema.test.ts b/src/functions/__tests__/schema.test.ts
index 418864d3..e75a15ae 100644
--- a/src/functions/__tests__/schema.test.ts
+++ b/src/functions/__tests__/schema.test.ts
@@ -2,7 +2,7 @@ import { Optional } from '@stoplight/types';
 import { JSONSchema4, JSONSchema6 } from 'json-schema';
 import { schema } from '../schema';
 
-function runSchema(target: any, schemaObj: object, oasVersion?: Optional<number>) {
+function runSchema(target: any, schemaObj: object, oasVersion?: Optional<2 | 3 | 3.1>) {
   return schema(target, { schema: schemaObj, oasVersion }, { given: [] }, { given: null, original: null } as any);
 }
 
diff --git a/src/rulesets/__tests__/message.test.ts b/src/rulesets/__tests__/message.test.ts
index 0b5f4c07..e51bfc46 100644
--- a/src/rulesets/__tests__/message.test.ts
+++ b/src/rulesets/__tests__/message.test.ts
@@ -8,6 +8,7 @@ describe('message util', () => {
         property: 'description',
         error: 'expected property to be truthy',
         path: '',
+        description: null,
         value: void 0,
       }),
     ).toEqual('oops... "description" is missing;error: expected property to be truthy');
@@ -20,6 +21,7 @@ describe('message util', () => {
         property: 'description',
         error: 'expected property to be truthy',
         path: '',
+        description: null,
         value,
       }),
     ).toEqual(`Value must not equal ${value}`);
@@ -32,6 +34,7 @@ describe('message util', () => {
         property: 'baz',
         error: 'foo',
         path: '',
+        description: null,
         value: void 0,
       }),
     ).toEqual('foofoobazfoofoo');
@@ -44,6 +47,7 @@ describe('message util', () => {
         property: 'description',
         error: 'expected property to be truthy',
         path: '',
+        description: null,
         value: void 0,
       }),
     ).toEqual('missing :(');
diff --git a/src/rulesets/__tests__/validation.test.ts b/src/rulesets/__tests__/validation.test.ts
index cde59420..3e3a051e 100644
--- a/src/rulesets/__tests__/validation.test.ts
+++ b/src/rulesets/__tests__/validation.test.ts
@@ -256,6 +256,777 @@ describe('Ruleset Validation', () => {
       }).toThrow(ValidationError);
     });
   });
+
+  describe('then validation', () => {
+    describe.each([
+      'casing',
+      'enumeration',
+      'length',
+      'pattern',
+      'schema',
+      'schema-path',
+      'unreferencedReusableObject',
+      'xor',
+    ])('%s function', name => {
+      it('complains about empty options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: name,
+                  functionOptions: {},
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about missing options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: name,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about nullified options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: name,
+                  functionOptions: null,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe.each(['truthy', 'falsy', 'undefined'])('%s function', name => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: name,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  field: 'test',
+                  function: name,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about the presence of functionOptions', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: name,
+                  functionOptions: {},
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('alphabetical function', () => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'alphabetical',
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  field: 'test',
+                  function: 'alphabetical',
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  field: 'test',
+                  function: 'alphabetical',
+                  functionOptions: {
+                    keyedBy: 'bar',
+                  },
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  field: 'test',
+                  function: 'alphabetical',
+                  functionOptions: null,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('does not complain about empty functionOptions', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'alphabetical',
+                  functionOptions: {},
+                },
+              },
+            },
+          }),
+        ).not.toThrow(ValidationError);
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'alphabetical',
+                  functionOptions: {
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about invalid keyedBy', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'alphabetical',
+                  functionOptions: {
+                    keyedBy: 2,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('casing function', () => {
+      it.each([
+        { type: 'cobol' },
+        { type: 'macro', disallowDigits: true },
+        { type: 'snake', disallowDigits: true, separator: { char: 'a' } },
+        { type: 'pascal', disallowDigits: false, separator: { char: 'b', allowLeading: true } },
+      ])('given valid then %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it.each([
+        { type: 'macro', foo: true },
+        { type: 'snake', separator: { char: 'a', foo: true } },
+      ])('complains about extra options %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about invalid type', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions: {
+                    type: 'foo',
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about missing char', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions: {
+                    type: 'pascal',
+                    disallowDigits: false,
+                    separator: {},
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions: {
+                    type: 'pascal',
+                    disallowDigits: false,
+                    separator: { allowLeading: true },
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about too length char', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions: {
+                    type: 'pascal',
+                    separator: {
+                      char: 'fo',
+                    },
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about too invalid char', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'casing',
+                  functionOptions: {
+                    type: 'pascal',
+                    separator: {
+                      char: null,
+                    },
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('enumeration function', () => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'enumeration',
+                  functionOptions: {
+                    values: ['foo', 2],
+                  },
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'enumeration',
+                  functionOptions: {
+                    values: ['foo', 2],
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it.each([[null], 2, null])('complains about invalid values %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'enumeration',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('length function', () => {
+      it.each([{ min: 2 }, { max: 4 }, { min: 2, max: 4 }])('given valid then %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'length',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'length',
+                  functionOptions: {
+                    min: 2,
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it.each([{ min: '2' }, { max: '2' }, { min: '4', max: '2' }])(
+        'complains about invalid options %s',
+        functionOptions => {
+          expect(
+            assertValidRuleset.bind(null, {
+              rules: {
+                rule: {
+                  given: '$',
+                  then: {
+                    function: 'length',
+                    functionOptions,
+                  },
+                },
+              },
+            }),
+          ).toThrow(ValidationError);
+        },
+      );
+    });
+
+    describe('pattern function', () => {
+      it.each([{ match: 'foo' }, { notMatch: 'foo' }, { match: 'foo', notMatch: 'bar' }])(
+        'given valid then %s',
+        functionOptions => {
+          expect(
+            assertValidRuleset.bind(null, {
+              rules: {
+                rule: {
+                  given: '$',
+                  then: {
+                    function: 'pattern',
+                    functionOptions,
+                  },
+                },
+              },
+            }),
+          ).not.toThrow();
+        },
+      );
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'pattern',
+                  functionOptions: {
+                    match: 'foo',
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it.each([{ match: 2 }, { notMatch: null }, { match: 4, notMatch: 'bar' }])(
+        'complains about invalid options %s',
+        functionOptions => {
+          expect(
+            assertValidRuleset.bind(null, {
+              rules: {
+                rule: {
+                  given: '$',
+                  then: {
+                    function: 'pattern',
+                    functionOptions,
+                  },
+                },
+              },
+            }),
+          ).toThrow(ValidationError);
+        },
+      );
+    });
+
+    describe('schema function', () => {
+      it.each([
+        { schema: { type: 'object' } },
+        { schema: { type: 'string' }, oasVersion: 2 },
+        { schema: { type: 'string' }, allErrors: true },
+        { schema: { type: 'string' }, oasVersion: 3.1, allErrors: false },
+      ])('given valid then %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'schema',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'schema',
+                  functionOptions: {
+                    schema: { type: 'object' },
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it.each([
+        { schema: { type: 'object' }, oasVersion: 1 },
+        { schema: { type: 'object' }, allErrors: null },
+        { schema: null, allErrors: null },
+      ])('complains about invalid options %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'schema',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('unreferencedReusableObject function', () => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'unreferencedReusableObject',
+                  functionOptions: {
+                    reusableObjectsLocation: 'foo',
+                  },
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'unreferencedReusableObject',
+                  functionOptions: {
+                    reusableObjectsLocation: 'foo',
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about invalid options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'unreferencedReusableObject',
+                  functionOptions: {
+                    reusableObjectsLocation: 2,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('xor function', () => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'xor',
+                  functionOptions: {
+                    properties: ['foo', 'bar'],
+                  },
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about extra options', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'xor',
+                  functionOptions: {
+                    properties: ['foo', 2],
+                    foo: true,
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it('complains about too few properties', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'xor',
+                  functionOptions: {
+                    properties: ['foo'],
+                  },
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+
+      it.each([[null], 2, null])('complains about invalid properties %s', functionOptions => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'xor',
+                  functionOptions,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+
+    describe('custom function', () => {
+      it('given valid then, does not complain', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'foo',
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  field: 'test',
+                  function: 'foo',
+                },
+              },
+            },
+          }),
+        ).not.toThrow();
+      });
+
+      it('complains about non-object functionOptions', () => {
+        expect(
+          assertValidRuleset.bind(null, {
+            rules: {
+              rule: {
+                given: '$',
+                then: {
+                  function: 'foo',
+                  functionOptions: true,
+                },
+              },
+            },
+          }),
+        ).toThrow(ValidationError);
+      });
+    });
+  });
 });
 
 describe('Function Validation', () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-empty-parameter.ts b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-empty-parameter.ts
index fda96a40..7748cf3f 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-empty-parameter.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-empty-parameter.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-channel-no-empty-parameter';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-query-nor-fragment.ts b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-query-nor-fragment.ts
index a541eb97..7fb084ec 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-query-nor-fragment.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-query-nor-fragment.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-channel-no-query-nor-fragment';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-trailing-slash.ts b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-trailing-slash.ts
index ad944000..6125e7ee 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-trailing-slash.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-channel-no-trailing-slash.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-channel-no-trailing-slash';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-headers-schema-type-object.ts b/src/rulesets/asyncapi/__tests__/asyncapi-headers-schema-type-object.ts
index bde140ba..7b01ad4d 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-headers-schema-type-object.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-headers-schema-type-object.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-headers-schema-type-object';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-info-contact-properties.ts b/src/rulesets/asyncapi/__tests__/asyncapi-info-contact-properties.ts
index 22b2f964..536e087c 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-info-contact-properties.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-info-contact-properties.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-info-contact-properties';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-info-contact.ts b/src/rulesets/asyncapi/__tests__/asyncapi-info-contact.ts
index 8b0dc6db..bf05e52d 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-info-contact.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-info-contact.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-info-contact';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-info-description.ts b/src/rulesets/asyncapi/__tests__/asyncapi-info-description.ts
index df2d9ce5..b34322e4 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-info-description.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-info-description.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-info-description';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-info-license-url.ts b/src/rulesets/asyncapi/__tests__/asyncapi-info-license-url.ts
index 22f6f009..bf251066 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-info-license-url.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-info-license-url.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-info-license-url';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-info-license.ts b/src/rulesets/asyncapi/__tests__/asyncapi-info-license.ts
index bfbc01d2..a447b526 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-info-license.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-info-license.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-info-license';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-operation-description.ts b/src/rulesets/asyncapi/__tests__/asyncapi-operation-description.ts
index f329e40b..968801e8 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-operation-description.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-operation-description.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-operation-description';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-operation-operationId.ts b/src/rulesets/asyncapi/__tests__/asyncapi-operation-operationId.ts
index 29b48303..fafbe31b 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-operation-operationId.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-operation-operationId.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-operation-operationId';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-parameter-description.ts b/src/rulesets/asyncapi/__tests__/asyncapi-parameter-description.ts
index 74ac28eb..e4560b6a 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-parameter-description.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-parameter-description.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-parameter-description';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-payload-default.ts b/src/rulesets/asyncapi/__tests__/asyncapi-payload-default.ts
index 5240e15f..9b45b0cc 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-payload-default.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-payload-default.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-payload-default';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-payload-examples.ts b/src/rulesets/asyncapi/__tests__/asyncapi-payload-examples.ts
index f5c89109..d67b8470 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-payload-examples.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-payload-examples.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-payload-examples';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-payload-unsupported-schemaFormat.ts b/src/rulesets/asyncapi/__tests__/asyncapi-payload-unsupported-schemaFormat.ts
index 416bdf17..6d8873bb 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-payload-unsupported-schemaFormat.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-payload-unsupported-schemaFormat.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-payload-unsupported-schemaFormat';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-payload.ts b/src/rulesets/asyncapi/__tests__/asyncapi-payload.ts
index 13fab5ca..77955dcb 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-payload.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-payload.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-payload';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-schema-default.ts b/src/rulesets/asyncapi/__tests__/asyncapi-schema-default.ts
index 4dec1912..3b0ba591 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-schema-default.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-schema-default.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-schema-default';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-schema-examples.ts b/src/rulesets/asyncapi/__tests__/asyncapi-schema-examples.ts
index 963e61b1..322b32dd 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-schema-examples.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-schema-examples.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-schema-examples';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-schema.ts b/src/rulesets/asyncapi/__tests__/asyncapi-schema.ts
index 43bcf1f6..ffc38b7a 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-schema.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-schema.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-schema';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-server-no-empty-variable.ts b/src/rulesets/asyncapi/__tests__/asyncapi-server-no-empty-variable.ts
index d0c07054..a1f135de 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-server-no-empty-variable.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-server-no-empty-variable.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-server-no-empty-variable';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-server-no-trailing-slash.ts b/src/rulesets/asyncapi/__tests__/asyncapi-server-no-trailing-slash.ts
index 0684f92f..e3b03109 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-server-no-trailing-slash.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-server-no-trailing-slash.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-server-no-trailing-slash';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-server-not-example-com.ts b/src/rulesets/asyncapi/__tests__/asyncapi-server-not-example-com.ts
index c8eee20d..136a7b3f 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-server-not-example-com.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-server-not-example-com.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-server-not-example-com';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-servers.ts b/src/rulesets/asyncapi/__tests__/asyncapi-servers.ts
index f42e13cc..c4748026 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-servers.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-servers.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-servers';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-tag-description.ts b/src/rulesets/asyncapi/__tests__/asyncapi-tag-description.ts
index d65ad29f..2db2adb4 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-tag-description.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-tag-description.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-tag-description';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-tags-alphabetical.ts b/src/rulesets/asyncapi/__tests__/asyncapi-tags-alphabetical.ts
index 129f8e1f..4b997130 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-tags-alphabetical.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-tags-alphabetical.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-tags-alphabetical';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-tags.ts b/src/rulesets/asyncapi/__tests__/asyncapi-tags.ts
index 6fb880ce..9d77920b 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-tags.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-tags.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-tags';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/__tests__/asyncapi-unused-components-schema.ts b/src/rulesets/asyncapi/__tests__/asyncapi-unused-components-schema.ts
index 80bbbf7e..caab3a42 100644
--- a/src/rulesets/asyncapi/__tests__/asyncapi-unused-components-schema.ts
+++ b/src/rulesets/asyncapi/__tests__/asyncapi-unused-components-schema.ts
@@ -1,12 +1,12 @@
 import { cloneDeep } from 'lodash';
 
 import { buildTestSpectralWithAsyncApiRule } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 import { Spectral } from '../../../spectral';
-import { IRunRule } from '../../../types';
 
 const ruleName = 'asyncapi-unused-components-schema';
 let s: Spectral;
-let rule: IRunRule;
+let rule: Rule;
 
 describe(`Rule '${ruleName}'`, () => {
   beforeEach(async () => {
diff --git a/src/rulesets/asyncapi/functions/__tests__/asyncApi2PayloadValidation.test.ts b/src/rulesets/asyncapi/functions/__tests__/asyncApi2PayloadValidation.test.ts
index 97383744..c86d68a4 100644
--- a/src/rulesets/asyncapi/functions/__tests__/asyncApi2PayloadValidation.test.ts
+++ b/src/rulesets/asyncapi/functions/__tests__/asyncApi2PayloadValidation.test.ts
@@ -1,11 +1,11 @@
 import { functions } from '../../../../functions';
 import { asyncApi2PayloadValidation } from '../asyncApi2PayloadValidation';
 
-function runPayloadValidation(targetVal: any, field: string) {
+function runPayloadValidation(targetVal: any) {
   return asyncApi2PayloadValidation.call(
     { functions },
     targetVal,
-    { field },
+    null,
     { given: ['$', 'components', 'messages', 'aMessage'] },
     { given: null, original: null, documentInventory: {} as any },
   );
@@ -18,7 +18,7 @@ describe('asyncApi2PayloadValidation', () => {
       deprecated: 14,
     };
 
-    const results = runPayloadValidation(payload, '$');
+    const results = runPayloadValidation(payload);
 
     expect(results).toEqual([
       {
diff --git a/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts b/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
index e1e44c1b..1e8dd22d 100644
--- a/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
+++ b/src/rulesets/oas/__tests__/oas2-valid-definition-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
 describe('oas2-valid-definition-example', () => {
-  const s = new Spectral();
-  s.registerFormat('oas2', () => true);
-  s.setRules({
-    'oas2-valid-definition-example': Object.assign(ruleset.rules['oas2-valid-definition-example'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['oas2-valid-definition-example'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas2', () => true);
+    s.setRules({
+      'oas2-valid-definition-example': Object.assign(ruleset.rules['oas2-valid-definition-example'], {
+        recommended: true,
+        type: RuleType[ruleset.rules['oas2-valid-definition-example'].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts b/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
index cd762581..48956eab 100644
--- a/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
+++ b/src/rulesets/oas/__tests__/oas2-valid-parameter-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
 describe('oas2-valid-parameter-example', () => {
-  const s = new Spectral();
-  s.registerFormat('oas2', () => true);
-  s.setRules({
-    'oas2-valid-parameter-example': Object.assign(ruleset.rules['oas2-valid-parameter-example'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['oas2-valid-parameter-example'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas2', () => true);
+    s.setRules({
+      'oas2-valid-parameter-example': Object.assign(ruleset.rules['oas2-valid-parameter-example'], {
+        recommended: true,
+        type: RuleType[ruleset.rules['oas2-valid-parameter-example'].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts b/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
index 00aebdb1..dcb4046d 100644
--- a/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-oas-parameter-example.ts
@@ -3,13 +3,18 @@ import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
 describe('oas3-valid-oas-parameter-example', () => {
-  const s = new Spectral();
-  s.registerFormat('oas3', () => true);
-  s.setRules({
-    'oas3-valid-oas-parameter-example': Object.assign(ruleset.rules['oas3-valid-oas-parameter-example'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['oas3-valid-oas-parameter-example'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+
+    s.registerFormat('oas3', () => true);
+    s.setRules({
+      'oas3-valid-oas-parameter-example': Object.assign(ruleset.rules['oas3-valid-oas-parameter-example'], {
+        recommended: true,
+        type: RuleType[ruleset.rules['oas3-valid-oas-parameter-example'].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts b/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
index 941ecdec..a89a7186 100644
--- a/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-parameter-schema-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
 describe('oas3-valid-parameter-schema-example', () => {
-  const s = new Spectral();
-  s.registerFormat('oas3', () => true);
-  s.setRules({
-    'oas3-valid-parameter-schema-example': Object.assign(ruleset.rules['oas3-valid-parameter-schema-example'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['oas3-valid-parameter-schema-example'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas3', () => true);
+    s.setRules({
+      'oas3-valid-parameter-schema-example': Object.assign(ruleset.rules['oas3-valid-parameter-schema-example'], {
+        recommended: true,
+        type: RuleType[ruleset.rules['oas3-valid-parameter-schema-example'].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts b/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
index 95aef983..3330e2be 100644
--- a/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
+++ b/src/rulesets/oas/__tests__/oas3-valid-schema-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../spectral';
 import * as ruleset from '../index.json';
 
 describe('oas3-valid-schema-example', () => {
-  const s = new Spectral();
-  s.registerFormat('oas3', () => true);
-  s.setRules({
-    'oas3-valid-schema-example': Object.assign(ruleset.rules['oas3-valid-schema-example'], {
-      recommended: true,
-      type: RuleType[ruleset.rules['oas3-valid-schema-example']!.type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas3', () => true);
+    s.setRules({
+      'oas3-valid-schema-example': Object.assign(ruleset.rules['oas3-valid-schema-example'], {
+        recommended: true,
+        type: RuleType[ruleset.rules['oas3-valid-schema-example']!.type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/templates/_oas-example.ts b/src/rulesets/oas/__tests__/templates/_oas-example.ts
index b72598bc..c6ebe383 100644
--- a/src/rulesets/oas/__tests__/templates/_oas-example.ts
+++ b/src/rulesets/oas/__tests__/templates/_oas-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../../spectral';
 import { rules } from '../../index.json';
 
 export default (ruleName: string, path: string) => {
-  const s = new Spectral();
-  s.registerFormat('oas3', () => true);
-  s.setRules({
-    [ruleName]: Object.assign(rules[ruleName], {
-      recommended: true,
-      type: RuleType[rules[ruleName].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas3', () => true);
+    s.setRules({
+      [ruleName]: Object.assign(rules[ruleName], {
+        recommended: true,
+        type: RuleType[rules[ruleName].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/__tests__/templates/_schema-example.ts b/src/rulesets/oas/__tests__/templates/_schema-example.ts
index 9c488455..a7c0211f 100644
--- a/src/rulesets/oas/__tests__/templates/_schema-example.ts
+++ b/src/rulesets/oas/__tests__/templates/_schema-example.ts
@@ -3,13 +3,17 @@ import { RuleType, Spectral } from '../../../../spectral';
 import { rules } from '../../index.json';
 
 export default (ruleName: string, path: string) => {
-  const s = new Spectral();
-  s.registerFormat('oas3', () => true);
-  s.setRules({
-    [ruleName]: Object.assign(rules[ruleName], {
-      recommended: true,
-      type: RuleType[rules[ruleName].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas3', () => true);
+    s.setRules({
+      [ruleName]: Object.assign(rules[ruleName], {
+        recommended: true,
+        type: RuleType[rules[ruleName].type],
+      }),
+    });
   });
 
   test('will pass when simple example is valid', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasOp2xxResponse.test.ts b/src/rulesets/oas/functions/__tests__/oasOp2xxResponse.test.ts
index 1523672a..232fda4a 100644
--- a/src/rulesets/oas/functions/__tests__/oasOp2xxResponse.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOp2xxResponse.test.ts
@@ -4,13 +4,17 @@ import { rules } from '../../index.json';
 import oasOp2xxResponse from '../oasOp2xxResponse';
 
 describe('oasOp2xxResponse', () => {
-  const s = new Spectral();
-  s.setFunctions({ oasOp2xxResponse });
-  s.setRules({
-    'operation-2xx-response': Object.assign(rules['operation-2xx-response'], {
-      recommended: true,
-      type: RuleType[rules['operation-2xx-response'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.setFunctions({ oasOp2xxResponse });
+    s.setRules({
+      'operation-2xx-response': Object.assign(rules['operation-2xx-response'], {
+        recommended: true,
+        type: RuleType[rules['operation-2xx-response'].type],
+      }),
+    });
   });
 
   test('validate a correct object', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts b/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
index 38da524b..8066f0d2 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpFormDataConsumeCheck.test.ts
@@ -4,14 +4,18 @@ import { rules } from '../../index.json';
 import oasOpFormDataConsumeCheck from '../oasOpFormDataConsumeCheck';
 
 describe('oasOpFormDataConsumeCheck', () => {
-  const s = new Spectral();
-  s.registerFormat('oas2', () => true);
-  s.setFunctions({ oasOpFormDataConsumeCheck });
-  s.setRules({
-    'oas2-operation-formData-consume-check': Object.assign(rules['oas2-operation-formData-consume-check'], {
-      recommended: true,
-      type: RuleType[rules['oas2-operation-formData-consume-check'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.registerFormat('oas2', () => true);
+    s.setFunctions({ oasOpFormDataConsumeCheck });
+    s.setRules({
+      'oas2-operation-formData-consume-check': Object.assign(rules['oas2-operation-formData-consume-check'], {
+        recommended: true,
+        type: RuleType[rules['oas2-operation-formData-consume-check'].type],
+      }),
+    });
   });
 
   test('validate a correct object', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasOpIdUnique.test.ts b/src/rulesets/oas/functions/__tests__/oasOpIdUnique.test.ts
index 814f5878..d2adde7d 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpIdUnique.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpIdUnique.test.ts
@@ -5,14 +5,17 @@ import { rules } from '../../index.json';
 import oasOpIdUnique from '../oasOpIdUnique';
 
 describe('oasOpIdUnique', () => {
-  const s = new Spectral();
+  let s: Spectral;
 
-  s.setFunctions({ oasOpIdUnique });
-  s.setRules({
-    'operation-operationId-unique': Object.assign(rules['operation-operationId-unique'], {
-      recommended: true,
-      type: RuleType[rules['operation-operationId-unique'].type],
-    }),
+  beforeEach(() => {
+    s = new Spectral();
+    s.setFunctions({ oasOpIdUnique });
+    s.setRules({
+      'operation-operationId-unique': Object.assign(rules['operation-operationId-unique'], {
+        recommended: true,
+        type: RuleType[rules['operation-operationId-unique'].type],
+      }),
+    });
   });
 
   test('validate a correct object', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasOpParams.test.ts b/src/rulesets/oas/functions/__tests__/oasOpParams.test.ts
index 1fe2674e..cc509295 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpParams.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpParams.test.ts
@@ -4,13 +4,17 @@ import { rules } from '../../index.json';
 import oasOpParams from '../oasOpParams';
 
 describe('oasOpParams', () => {
-  const s = new Spectral();
-  s.setFunctions({ oasOpParams });
-  s.setRules({
-    'operation-parameters': Object.assign(rules['operation-parameters'], {
-      recommended: true,
-      type: RuleType[rules['operation-parameters'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.setFunctions({ oasOpParams });
+    s.setRules({
+      'operation-parameters': Object.assign(rules['operation-parameters'], {
+        recommended: true,
+        type: RuleType[rules['operation-parameters'].type],
+      }),
+    });
   });
 
   test('No error if no params', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts b/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
index d1071209..26367295 100644
--- a/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasOpSecurityDefined.test.ts
@@ -5,14 +5,18 @@ import oasOpSecurityDefined from '../oasOpSecurityDefined';
 
 describe('oasOpSecurityDefined', () => {
   describe('oas2', () => {
-    const s = new Spectral();
-    s.registerFormat('oas2', () => true);
-    s.setFunctions({ oasOpSecurityDefined });
-    s.setRules({
-      'oas2-operation-security-defined': Object.assign(oasRules['oas2-operation-security-defined'], {
-        recommended: true,
-        type: RuleType[oasRules['oas2-operation-security-defined'].type],
-      }),
+    let s: Spectral;
+
+    beforeEach(() => {
+      s = new Spectral();
+      s.registerFormat('oas2', () => true);
+      s.setFunctions({ oasOpSecurityDefined });
+      s.setRules({
+        'oas2-operation-security-defined': Object.assign(oasRules['oas2-operation-security-defined'], {
+          recommended: true,
+          type: RuleType[oasRules['oas2-operation-security-defined'].type],
+        }),
+      });
     });
 
     test('validate a correct object (just in body)', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/oasPathParam.test.ts b/src/rulesets/oas/functions/__tests__/oasPathParam.test.ts
index 931f5f00..53261b10 100644
--- a/src/rulesets/oas/functions/__tests__/oasPathParam.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasPathParam.test.ts
@@ -1,16 +1,20 @@
 import { DiagnosticSeverity } from '@stoplight/types';
-import { RuleType, Spectral } from '../../../../index';
+import { Document, Parsers, RuleType, Spectral } from '../../../../index';
 import { rules } from '../../index.json';
 import oasPathParam from '../oasPathParam';
 
 describe('oasPathParam', () => {
-  const s = new Spectral();
-  s.setFunctions({ oasPathParam });
-  s.setRules({
-    'path-params': Object.assign(rules['path-params'], {
-      recommended: true,
-      type: RuleType[rules['path-params'].type],
-    }),
+  let s: Spectral;
+
+  beforeEach(() => {
+    s = new Spectral();
+    s.setFunctions({ oasPathParam });
+    s.setRules({
+      'path-params': Object.assign(rules['path-params'], {
+        recommended: true,
+        type: RuleType[rules['path-params'].type],
+      }),
+    });
   });
 
   test('No error if templated path is not used', async () => {
@@ -255,44 +259,49 @@ describe('oasPathParam', () => {
   });
 
   test('Error if paths are functionally equivalent', async () => {
-    const results = await s.run({
-      paths: {
-        '/foo/{boo}': {
-          parameters: [
-            {
-              name: 'boo',
-              in: 'path',
-              required: true,
-            },
-          ],
-          get: {},
-        },
-        '/foo/{bar}': {
-          parameters: [
-            {
-              name: 'bar',
-              in: 'path',
-              required: true,
-            },
-          ],
-          get: {},
-        },
-      },
-    });
+    const results = await s.run(
+      new Document(
+        `{
+  "paths": {
+    "/foo/{boo}": {
+      "parameters": [
+        {
+          "name": "boo",
+          "in": "path",
+          "required": true
+        }
+      ],
+      "get": {}
+    },
+    "/foo/{bar}": {
+      "parameters": [
+        {
+          "name": "bar",
+          "in": "path",
+          "required": true
+        }
+      ],
+      "get": {}
+    }
+  }
+}`,
+        Parsers.Json,
+      ),
+    );
 
     expect(results).toEqual([
       {
         code: 'path-params',
         message: `The paths \`/foo/{boo}\` and \`/foo/{bar}\` are equivalent.`,
-        path: ['paths'],
+        path: ['paths', '/foo/{bar}'],
         range: {
           end: {
-            character: 15,
-            line: 20,
+            character: 5,
+            line: 21,
           },
           start: {
-            character: 10,
-            line: 1,
+            character: 18,
+            line: 12,
           },
         },
         severity: DiagnosticSeverity.Error,
diff --git a/src/rulesets/oas/functions/__tests__/oasTagDefined.test.ts b/src/rulesets/oas/functions/__tests__/oasTagDefined.test.ts
index 58d16182..f272628a 100644
--- a/src/rulesets/oas/functions/__tests__/oasTagDefined.test.ts
+++ b/src/rulesets/oas/functions/__tests__/oasTagDefined.test.ts
@@ -5,14 +5,17 @@ import { rules } from '../../index.json';
 import oasTagDefined from '../oasTagDefined';
 
 describe('oasTagDefined', () => {
-  const s = new Spectral();
+  let s: Spectral;
 
-  s.setFunctions({ oasTagDefined });
-  s.setRules({
-    'operation-tag-defined': Object.assign(rules['operation-tag-defined'], {
-      recommended: true,
-      type: RuleType[rules['operation-tag-defined'].type],
-    }),
+  beforeEach(() => {
+    s = new Spectral();
+    s.setFunctions({ oasTagDefined });
+    s.setRules({
+      'operation-tag-defined': Object.assign(rules['operation-tag-defined'], {
+        recommended: true,
+        type: RuleType[rules['operation-tag-defined'].type],
+      }),
+    });
   });
 
   test('validate a correct object', async () => {
diff --git a/src/rulesets/oas/functions/__tests__/typedEnum.test.ts b/src/rulesets/oas/functions/__tests__/typedEnum.test.ts
index 4155c44a..22168bdb 100644
--- a/src/rulesets/oas/functions/__tests__/typedEnum.test.ts
+++ b/src/rulesets/oas/functions/__tests__/typedEnum.test.ts
@@ -1,13 +1,11 @@
 import { functions } from '../../../../functions';
 import { typedEnum } from '../typedEnum';
 
-const defaultReportingThreshold = 3;
-
-function runTypedEnum(targetVal: any, reportingThreshold: any) {
+function runTypedEnum(targetVal: any) {
   return typedEnum.call(
     { functions },
     targetVal,
-    { reportingThreshold },
+    null,
     { given: ['$'] },
     { given: null, original: null, documentInventory: {} as any },
   );
@@ -21,7 +19,7 @@ describe('typedEnum', () => {
         enum: enumContent,
       };
 
-      expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+      expect(runTypedEnum(schema)).toBeUndefined();
     });
   });
 
@@ -31,7 +29,7 @@ describe('typedEnum', () => {
       enum: [],
     };
 
-    expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+    expect(runTypedEnum(schema)).toBeUndefined();
   });
 
   describe('basic', () => {
@@ -41,7 +39,7 @@ describe('typedEnum', () => {
         enum: [123, 456],
       };
 
-      expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+      expect(runTypedEnum(schema)).toBeUndefined();
     });
 
     test('is undefined when all enum values respect the type', () => {
@@ -50,7 +48,7 @@ describe('typedEnum', () => {
         enum: [123, 456],
       };
 
-      expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+      expect(runTypedEnum(schema)).toBeUndefined();
     });
 
     test.each([undefined])('is undefined when type is "%s"', (typeValue: unknown) => {
@@ -59,7 +57,7 @@ describe('typedEnum', () => {
         enum: [123, 456],
       };
 
-      expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+      expect(runTypedEnum(schema)).toBeUndefined();
     });
 
     test('identifies enum values which do not respect the type', () => {
@@ -68,7 +66,7 @@ describe('typedEnum', () => {
         enum: [123, 'a string!', 456, 'and another one!'],
       };
 
-      expect(runTypedEnum(schema, defaultReportingThreshold)).toEqual([
+      expect(runTypedEnum(schema)).toEqual([
         {
           message: 'Enum value `a string!` does not respect the specified type `integer`.',
           path: ['$', 'enum', 1],
@@ -97,7 +95,7 @@ describe('typedEnum', () => {
           enum: valids,
         };
 
-        expect(runTypedEnum(schema, defaultReportingThreshold)).toBeUndefined();
+        expect(runTypedEnum(schema)).toBeUndefined();
       },
     );
 
@@ -109,7 +107,7 @@ describe('typedEnum', () => {
           enum: [valids[0], invalid],
         };
 
-        const results = runTypedEnum(schema, defaultReportingThreshold);
+        const results = runTypedEnum(schema);
 
         expect(results[0].message).toContain(`value \`${invalid}\``);
       },
diff --git a/src/utils/__tests__/getLintTargets.test.ts b/src/runner/utils/__tests__/getLintTargets.test.ts
similarity index 100%
rename from src/utils/__tests__/getLintTargets.test.ts
rename to src/runner/utils/__tests__/getLintTargets.test.ts
diff --git a/src/utils/__tests__/pivotExceptions.test.ts b/src/runner/utils/__tests__/pivotExceptions.test.ts
similarity index 80%
rename from src/utils/__tests__/pivotExceptions.test.ts
rename to src/runner/utils/__tests__/pivotExceptions.test.ts
index 282f8906..17539596 100644
--- a/src/utils/__tests__/pivotExceptions.test.ts
+++ b/src/runner/utils/__tests__/pivotExceptions.test.ts
@@ -1,19 +1,22 @@
 import { DiagnosticSeverity, Dictionary } from '@stoplight/types';
-import { InvalidUriError } from '../../rulesets/mergers/exceptions';
-import { IRunRule } from '../../types';
+import { InvalidUriError } from '../../../rulesets/mergers/exceptions';
 import { IExceptionLocation, pivotExceptions } from '../pivotExceptions';
 
-import { buildRulesetExceptionCollectionFrom } from '../../../setupTests';
+import { buildRulesetExceptionCollectionFrom } from '../../../../setupTests';
+import { Rule } from '../../../rule';
 
 describe('pivotExceptions', () => {
-  const dummyRule: IRunRule = {
-    name: '',
-    severity: DiagnosticSeverity.Error,
-    given: '',
-    then: {
-      function: 'truthy',
-    },
-  };
+  let dummyRule: Rule;
+
+  beforeEach(() => {
+    dummyRule = new Rule('', {
+      severity: DiagnosticSeverity.Error,
+      given: '',
+      then: {
+        function: 'truthy',
+      },
+    });
+  });
 
   it('ignores exceptions for rules that are not part of the run', () => {
     const exceptions = {