Repository: nightwatchjs/nightwatch
Pull request #3730 (2023-05-16 15:39:41)

chore: Add Github Action and husky
To maintain consistency in Nightwatch contributions, this PR now requires contributors to update the "types" when submitting a pull request through Github Action. If the "types" are not found in the PR, Github Action will provide a comment as a reminder. Once the user has added the necessary types, Github Bot will update the comment accordingly.

I've included "husky" as a pre-commit tool to guarantee that our code adheres to eslint standards and maintains quality.

### Error Scenario (User hasn't updated types)
![image](https://github.com/nightwatchjs/nightwatch/assets/8705386/d943e170-a178-4fd5-ac8a-090ed341ddb6)


### Success Scenario (User has updated types)
![image](https://github.com/nightwatchjs/nightwatch/assets/8705386/4965e955-2727-452e-9fd3-3cfb4a787d3b)

## Requirement
We need to update Action Permission for this group
![image](https://github.com/nightwatchjs/nightwatch/assets/8705386/19748834-7a04-4d7e-9d8c-0da6d9e78794)



garg3133: Maybe instead of directly throwing an error, we should ask the committer if they want to continue without adding the types, because we won't be adding types in every commit we create, but mostly as a separate commit towards the end, just like we don't add tests in every commit but have a separate commit for tests.

Also, I think it would be much better if instead of having this as a pre-commit hook, we could enable some GitHub bot which throws a warning if types are not added in the PR, something like what [typescript-bot does for tests](https://github.com/DefinitelyTyped/DefinitelyTyped/pull/64715#issuecomment-1468062803) (see "Code Reviews" section).

Patch:
diff --git a/.github/workflows/types-checker.yml b/.github/workflows/types-checker.yml
new file mode 100644
index 00000000..c1b79ad9
--- /dev/null
+++ b/.github/workflows/types-checker.yml
@@ -0,0 +1,77 @@
+name: Missing Types Checker
+
+on:
+  pull_request:
+    types:
+      - synchronize
+
+jobs:
+  detect-modified-files:
+    runs-on: ubuntu-latest
+    steps:
+      - name: Checkout repository
+        uses: actions/checkout@v2
+        with:
+            fetch-depth: 0
+
+      - name: Get modified files
+        id: modified-files
+        run: |
+          echo $(git diff --name-only HEAD^ types/)
+          echo $(git diff --name-only main types/)
+          echo "::set-output name=modified::$(git diff --name-only HEAD^ types/)"
+      
+      - name: Comment on PR
+        if: steps.modified-files.outputs.modified == ''
+        id: comment-on-pr
+        uses: actions/github-script@v6
+        with:
+          script: |
+            const comment = `
+            ## Status
+            * ❌ No modified files found in the **types** directory.
+
+            Please make sure to include types for any changes you have made. Thank you!.`;
+            const pullRequest = await github.rest.pulls.get({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              pull_number: context.issue.number
+            });
+            const comments = await github.rest.issues.listComments({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.issue.number
+            });
+            const existingComment = comments.data.find(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('No modified files found in the **types** director'));
+            if (existingComment) {
+              console.log('Comment already exists');
+            } else {
+              await github.rest.issues.createComment({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                issue_number: context.issue.number,
+                body: comment
+              });
+            }
+      - name: Edit comment on PR
+        if: steps.modified-files.outputs.modified != ''
+        uses: actions/github-script@v6
+        with:
+          script: |
+            const comment = `
+            ## Status
+            * :white_check_mark: Type files updated!`;
+            const comments = await github.rest.issues.listComments({
+              owner: context.repo.owner,
+              repo: context.repo.repo,
+              issue_number: context.issue.number
+            });
+            const existingComment = comments.data.find(comment => comment.user.login === 'github-actions[bot]' && comment.body.includes('No modified files found in the **types** director'));
+            if (existingComment) {
+              await github.rest.issues.updateComment({
+                owner: context.repo.owner,
+                repo: context.repo.repo,
+                comment_id: existingComment.id,
+                body: comment
+              });
+            }
diff --git a/.husky/pre-commit b/.husky/pre-commit
new file mode 100755
index 00000000..36af2198
--- /dev/null
+++ b/.husky/pre-commit
@@ -0,0 +1,4 @@
+#!/bin/sh
+. "$(dirname "$0")/_/husky.sh"
+
+npx lint-staged
diff --git a/lib/api/_loaders/assertion.js b/lib/api/_loaders/assertion.js
index 455a429b..1da40ce7 100644
--- a/lib/api/_loaders/assertion.js
+++ b/lib/api/_loaders/assertion.js
@@ -1,3 +1,5 @@
+const {WebElementPromise} = require('selenium-webdriver');
+
 const BaseCommandLoader = require('./_command-loader.js');
 const Utils = require('../../utils');
 const AssertionInstance = require('../assertions/_assertionInstance.js');
@@ -77,7 +79,7 @@ class AssertionLoader extends BaseCommandLoader {
 
       // we only should return a Promise if not a nightwatch element, otherwise we risk breaking changes
       const element = args[0];
-      const shouldReturnPromise = element && (element.sessionId && element.elementId || element.driver_ && element.id_);
+      const shouldReturnPromise = element && !(element instanceof WebElementPromise) && (element.sessionId && element.elementId || element.driver_ && element.id_);
       const deferred = Utils.createPromise();
       const isES6Async = shouldReturnPromise || nightwatchInstance.settings.always_async_commands ||
         (Utils.isUndefined(this.isES6Async) ? nightwatchInstance.isES6AsyncTestcase : this.isES6Async);
diff --git a/lib/api/web-element/assert/element-assertions.js b/lib/api/web-element/assert/element-assertions.js
index 59360c48..138b39d5 100644
--- a/lib/api/web-element/assert/element-assertions.js
+++ b/lib/api/web-element/assert/element-assertions.js
@@ -11,7 +11,7 @@ class ScopedElementAssertions {
     const assert = this.nightwatchInstance.api.assert;
 
     await callback(this.negated ? assert.not : assert, this.scopedElement.webElement);
-
+    
     return this.scopedElement;
   }
 
diff --git a/package-lock.json b/package-lock.json
index df4941e8..262bc681 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -56,8 +56,10 @@
         "@types/node": "^18.11.7",
         "coveralls": "^3.1.1",
         "eslint": "^8.9.0",
+        "husky": "^8.0.0",
         "is-ci": "^3.0.1",
         "js-yaml": "^3.13.1",
+        "lint-staged": "^13.2.2",
         "lodash": "^4.17.21",
         "mocha-junit-reporter": "^2.0.2",
         "mochawesome": "^7.1.3",
@@ -1580,6 +1582,15 @@
         "node": ">=0.3.1"
       }
     },
+    "node_modules/astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/async": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
@@ -2067,6 +2078,72 @@
         "@colors/colors": "1.5.0"
       }
     },
+    "node_modules/cli-truncate": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
+      "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
+      "dev": true,
+      "dependencies": {
+        "slice-ansi": "^5.0.0",
+        "string-width": "^5.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate/node_modules/ansi-regex": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+      "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-regex?sponsor=1"
+      }
+    },
+    "node_modules/cli-truncate/node_modules/emoji-regex": {
+      "version": "9.2.2",
+      "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+      "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+      "dev": true
+    },
+    "node_modules/cli-truncate/node_modules/string-width": {
+      "version": "5.1.2",
+      "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+      "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+      "dev": true,
+      "dependencies": {
+        "eastasianwidth": "^0.2.0",
+        "emoji-regex": "^9.2.2",
+        "strip-ansi": "^7.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/cli-truncate/node_modules/strip-ansi": {
+      "version": "7.0.1",
+      "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
+      "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
+      "dev": true,
+      "dependencies": {
+        "ansi-regex": "^6.0.1"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/strip-ansi?sponsor=1"
+      }
+    },
     "node_modules/clipboardy": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
@@ -2117,6 +2194,12 @@
       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
       "dev": true
     },
+    "node_modules/colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true
+    },
     "node_modules/combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -3135,18 +3218,6 @@
         "url": "https://github.com/sindresorhus/execa?sponsor=1"
       }
     },
-    "node_modules/execa/node_modules/get-stream": {
-      "version": "6.0.1",
-      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-      "dev": true,
-      "engines": {
-        "node": ">=10"
-      },
-      "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
-      }
-    },
     "node_modules/ext": {
       "version": "1.7.0",
       "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz",
@@ -3542,6 +3613,18 @@
         "node": ">=8.0.0"
       }
     },
+    "node_modules/get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -3861,6 +3944,21 @@
         "node": ">=10.17.0"
       }
     },
+    "node_modules/husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true,
+      "bin": {
+        "husky": "lib/bin.js"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/typicode"
+      }
+    },
     "node_modules/iconv-lite": {
       "version": "0.6.3",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -4864,12 +4962,254 @@
         "immediate": "~3.0.5"
       }
     },
+    "node_modules/lilconfig": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+      "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
     "node_modules/lines-and-columns": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
       "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
       "dev": true
     },
+    "node_modules/lint-staged": {
+      "version": "13.2.2",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz",
+      "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==",
+      "dev": true,
+      "dependencies": {
+        "chalk": "5.2.0",
+        "cli-truncate": "^3.1.0",
+        "commander": "^10.0.0",
+        "debug": "^4.3.4",
+        "execa": "^7.0.0",
+        "lilconfig": "2.1.0",
+        "listr2": "^5.0.7",
+        "micromatch": "^4.0.5",
+        "normalize-path": "^3.0.0",
+        "object-inspect": "^1.12.3",
+        "pidtree": "^0.6.0",
+        "string-argv": "^0.3.1",
+        "yaml": "^2.2.2"
+      },
+      "bin": {
+        "lint-staged": "bin/lint-staged.js"
+      },
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://opencollective.com/lint-staged"
+      }
+    },
+    "node_modules/lint-staged/node_modules/chalk": {
+      "version": "5.2.0",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
+      "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/lint-staged/node_modules/commander": {
+      "version": "10.0.1",
+      "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+      "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+      "dev": true,
+      "engines": {
+        "node": ">=14"
+      }
+    },
+    "node_modules/lint-staged/node_modules/execa": {
+      "version": "7.1.1",
+      "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz",
+      "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==",
+      "dev": true,
+      "dependencies": {
+        "cross-spawn": "^7.0.3",
+        "get-stream": "^6.0.1",
+        "human-signals": "^4.3.0",
+        "is-stream": "^3.0.0",
+        "merge-stream": "^2.0.0",
+        "npm-run-path": "^5.1.0",
+        "onetime": "^6.0.0",
+        "signal-exit": "^3.0.7",
+        "strip-final-newline": "^3.0.0"
+      },
+      "engines": {
+        "node": "^14.18.0 || ^16.14.0 || >=18.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sindresorhus/execa?sponsor=1"
+      }
+    },
+    "node_modules/lint-staged/node_modules/human-signals": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+      "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=14.18.0"
+      }
+    },
+    "node_modules/lint-staged/node_modules/is-stream": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+      "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lint-staged/node_modules/mimic-fn": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+      "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lint-staged/node_modules/npm-run-path": {
+      "version": "5.1.0",
+      "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+      "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+      "dev": true,
+      "dependencies": {
+        "path-key": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lint-staged/node_modules/onetime": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+      "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+      "dev": true,
+      "dependencies": {
+        "mimic-fn": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lint-staged/node_modules/path-key": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+      "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/lint-staged/node_modules/strip-final-newline": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+      "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/listr2": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz",
+      "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==",
+      "dev": true,
+      "dependencies": {
+        "cli-truncate": "^2.1.0",
+        "colorette": "^2.0.19",
+        "log-update": "^4.0.0",
+        "p-map": "^4.0.0",
+        "rfdc": "^1.3.0",
+        "rxjs": "^7.8.0",
+        "through": "^2.3.8",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": "^14.13.1 || >=16.0.0"
+      },
+      "peerDependencies": {
+        "enquirer": ">= 2.3.0 < 3"
+      },
+      "peerDependenciesMeta": {
+        "enquirer": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/listr2/node_modules/cli-truncate": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+      "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+      "dev": true,
+      "dependencies": {
+        "slice-ansi": "^3.0.0",
+        "string-width": "^4.2.0"
+      },
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/listr2/node_modules/p-map": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+      "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+      "dev": true,
+      "dependencies": {
+        "aggregate-error": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/listr2/node_modules/slice-ansi": {
+      "version": "3.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+      "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
     "node_modules/locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -5073,19 +5413,68 @@
         "node": ">=0.8.6"
       }
     },
-    "node_modules/log-symbols": {
-      "version": "4.1.0",
-      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
-      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+    "node_modules/log-symbols": {
+      "version": "4.1.0",
+      "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz",
+      "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==",
+      "dependencies": {
+        "chalk": "^4.1.0",
+        "is-unicode-supported": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+      "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
+      "dev": true,
+      "dependencies": {
+        "ansi-escapes": "^4.3.0",
+        "cli-cursor": "^3.1.0",
+        "slice-ansi": "^4.0.0",
+        "wrap-ansi": "^6.2.0"
+      },
+      "engines": {
+        "node": ">=10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/log-update/node_modules/slice-ansi": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+      "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+      "dev": true,
       "dependencies": {
-        "chalk": "^4.1.0",
-        "is-unicode-supported": "^0.1.0"
+        "ansi-styles": "^4.0.0",
+        "astral-regex": "^2.0.0",
+        "is-fullwidth-code-point": "^3.0.0"
       },
       "engines": {
         "node": ">=10"
       },
       "funding": {
-        "url": "https://github.com/sponsors/sindresorhus"
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/log-update/node_modules/wrap-ansi": {
+      "version": "6.2.0",
+      "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+      "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^4.0.0",
+        "string-width": "^4.1.0",
+        "strip-ansi": "^6.0.0"
+      },
+      "engines": {
+        "node": ">=8"
       }
     },
     "node_modules/loose-envify": {
@@ -6465,6 +6854,18 @@
         "url": "https://github.com/sponsors/jonschlinkert"
       }
     },
+    "node_modules/pidtree": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+      "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+      "dev": true,
+      "bin": {
+        "pidtree": "bin/pidtree.js"
+      },
+      "engines": {
+        "node": ">=0.10"
+      }
+    },
     "node_modules/piscina": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz",
@@ -7146,6 +7547,12 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+      "dev": true
+    },
     "node_modules/rimraf": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -7609,6 +8016,46 @@
         "node": ">=8"
       }
     },
+    "node_modules/slice-ansi": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+      "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+      "dev": true,
+      "dependencies": {
+        "ansi-styles": "^6.0.0",
+        "is-fullwidth-code-point": "^4.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/slice-ansi?sponsor=1"
+      }
+    },
+    "node_modules/slice-ansi/node_modules/ansi-styles": {
+      "version": "6.2.1",
+      "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+      "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/ansi-styles?sponsor=1"
+      }
+    },
+    "node_modules/slice-ansi/node_modules/is-fullwidth-code-point": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+      "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -7957,6 +8404,12 @@
         "node": ">=0.8"
       }
     },
+    "node_modules/through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
     "node_modules/tmp": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -8633,6 +9086,15 @@
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
     },
+    "node_modules/yaml": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
+      "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
+      "dev": true,
+      "engines": {
+        "node": ">= 14"
+      }
+    },
     "node_modules/yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
@@ -9954,6 +10416,12 @@
         }
       }
     },
+    "astral-regex": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz",
+      "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==",
+      "dev": true
+    },
     "async": {
       "version": "3.2.4",
       "resolved": "https://registry.npmjs.org/async/-/async-3.2.4.tgz",
@@ -10276,6 +10744,50 @@
         "string-width": "^4.2.0"
       }
     },
+    "cli-truncate": {
+      "version": "3.1.0",
+      "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-3.1.0.tgz",
+      "integrity": "sha512-wfOBkjXteqSnI59oPcJkcPl/ZmwvMMOj340qUIY1SKZCv0B9Cf4D4fAucRkIKQmsIuYK3x1rrgU7MeGRruiuiA==",
+      "dev": true,
+      "requires": {
+        "slice-ansi": "^5.0.0",
+        "string-width": "^5.0.0"
+      },
+      "dependencies": {
+        "ansi-regex": {
+          "version": "6.0.1",
+          "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz",
+          "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==",
+          "dev": true
+        },
+        "emoji-regex": {
+          "version": "9.2.2",
+          "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz",
+          "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==",
+          "dev": true
+        },
+        "string-width": {
+          "version": "5.1.2",
+          "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz",
+          "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==",
+          "dev": true,
+          "requires": {
+            "eastasianwidth": "^0.2.0",
+            "emoji-regex": "^9.2.2",
+            "strip-ansi": "^7.0.1"
+          }
+        },
+        "strip-ansi": {
+          "version": "7.0.1",
+          "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.0.1.tgz",
+          "integrity": "sha512-cXNxvT8dFNRVfhVME3JAe98mkXDYN2O1l7jmcwMnOslDeESg1rF/OZMtK0nRAhiari1unG5cD4jG3rapUAkLbw==",
+          "dev": true,
+          "requires": {
+            "ansi-regex": "^6.0.1"
+          }
+        }
+      }
+    },
     "clipboardy": {
       "version": "3.0.0",
       "resolved": "https://registry.npmjs.org/clipboardy/-/clipboardy-3.0.0.tgz",
@@ -10317,6 +10829,12 @@
       "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==",
       "dev": true
     },
+    "colorette": {
+      "version": "2.0.20",
+      "resolved": "https://registry.npmjs.org/colorette/-/colorette-2.0.20.tgz",
+      "integrity": "sha512-IfEDxwoWIjkeXL1eXcDiow4UbKjhLdq6/EuSVR9GMN7KVH3r9gQ83e73hsz1Nd1T3ijd5xv1wcWRYO+D6kCI2w==",
+      "dev": true
+    },
     "combined-stream": {
       "version": "1.0.8",
       "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz",
@@ -11101,14 +11619,6 @@
         "onetime": "^5.1.2",
         "signal-exit": "^3.0.3",
         "strip-final-newline": "^2.0.0"
-      },
-      "dependencies": {
-        "get-stream": {
-          "version": "6.0.1",
-          "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
-          "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
-          "dev": true
-        }
       }
     },
     "ext": {
@@ -11408,6 +11918,12 @@
       "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==",
       "dev": true
     },
+    "get-stream": {
+      "version": "6.0.1",
+      "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz",
+      "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==",
+      "dev": true
+    },
     "getpass": {
       "version": "0.1.7",
       "resolved": "https://registry.npmjs.org/getpass/-/getpass-0.1.7.tgz",
@@ -11637,6 +12153,12 @@
       "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==",
       "dev": true
     },
+    "husky": {
+      "version": "8.0.3",
+      "resolved": "https://registry.npmjs.org/husky/-/husky-8.0.3.tgz",
+      "integrity": "sha512-+dQSyqPh4x1hlO1swXBiNb2HzTDN1I2IGLQx1GrBuiqFJfoMrnZWwVmatvSiO+Iz8fBUnf+lekwNo4c2LlXItg==",
+      "dev": true
+    },
     "iconv-lite": {
       "version": "0.6.3",
       "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz",
@@ -12382,12 +12904,166 @@
         "immediate": "~3.0.5"
       }
     },
+    "lilconfig": {
+      "version": "2.1.0",
+      "resolved": "https://registry.npmjs.org/lilconfig/-/lilconfig-2.1.0.tgz",
+      "integrity": "sha512-utWOt/GHzuUxnLKxB6dk81RoOeoNeHgbrXiuGk4yyF5qlRz+iIVWu56E2fqGHFrXz0QNUhLB/8nKqvRH66JKGQ==",
+      "dev": true
+    },
     "lines-and-columns": {
       "version": "1.2.4",
       "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz",
       "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==",
       "dev": true
     },
+    "lint-staged": {
+      "version": "13.2.2",
+      "resolved": "https://registry.npmjs.org/lint-staged/-/lint-staged-13.2.2.tgz",
+      "integrity": "sha512-71gSwXKy649VrSU09s10uAT0rWCcY3aewhMaHyl2N84oBk4Xs9HgxvUp3AYu+bNsK4NrOYYxvSgg7FyGJ+jGcA==",
+      "dev": true,
+      "requires": {
+        "chalk": "5.2.0",
+        "cli-truncate": "^3.1.0",
+        "commander": "^10.0.0",
+        "debug": "^4.3.4",
+        "execa": "^7.0.0",
+        "lilconfig": "2.1.0",
+        "listr2": "^5.0.7",
+        "micromatch": "^4.0.5",
+        "normalize-path": "^3.0.0",
+        "object-inspect": "^1.12.3",
+        "pidtree": "^0.6.0",
+        "string-argv": "^0.3.1",
+        "yaml": "^2.2.2"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "5.2.0",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.2.0.tgz",
+          "integrity": "sha512-ree3Gqw/nazQAPuJJEy+avdl7QfZMcUvmHIKgEZkGL+xOBzRvup5Hxo6LHuMceSxOabuJLJm5Yp/92R9eMmMvA==",
+          "dev": true
+        },
+        "commander": {
+          "version": "10.0.1",
+          "resolved": "https://registry.npmjs.org/commander/-/commander-10.0.1.tgz",
+          "integrity": "sha512-y4Mg2tXshplEbSGzx7amzPwKKOCGuoSRP/CjEdwwk0FOGlUbq6lKuoyDZTNZkmxHdJtp54hdfY/JUrdL7Xfdug==",
+          "dev": true
+        },
+        "execa": {
+          "version": "7.1.1",
+          "resolved": "https://registry.npmjs.org/execa/-/execa-7.1.1.tgz",
+          "integrity": "sha512-wH0eMf/UXckdUYnO21+HDztteVv05rq2GXksxT4fCGeHkBhw1DROXh40wcjMcRqDOWE7iPJ4n3M7e2+YFP+76Q==",
+          "dev": true,
+          "requires": {
+            "cross-spawn": "^7.0.3",
+            "get-stream": "^6.0.1",
+            "human-signals": "^4.3.0",
+            "is-stream": "^3.0.0",
+            "merge-stream": "^2.0.0",
+            "npm-run-path": "^5.1.0",
+            "onetime": "^6.0.0",
+            "signal-exit": "^3.0.7",
+            "strip-final-newline": "^3.0.0"
+          }
+        },
+        "human-signals": {
+          "version": "4.3.1",
+          "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-4.3.1.tgz",
+          "integrity": "sha512-nZXjEF2nbo7lIw3mgYjItAfgQXog3OjJogSbKa2CQIIvSGWcKgeJnQlNXip6NglNzYH45nSRiEVimMvYL8DDqQ==",
+          "dev": true
+        },
+        "is-stream": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-3.0.0.tgz",
+          "integrity": "sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==",
+          "dev": true
+        },
+        "mimic-fn": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-4.0.0.tgz",
+          "integrity": "sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==",
+          "dev": true
+        },
+        "npm-run-path": {
+          "version": "5.1.0",
+          "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-5.1.0.tgz",
+          "integrity": "sha512-sJOdmRGrY2sjNTRMbSvluQqg+8X7ZK61yvzBEIDhz4f8z1TZFYABsqjjCBd/0PUNE9M6QDgHJXQkGUEm7Q+l9Q==",
+          "dev": true,
+          "requires": {
+            "path-key": "^4.0.0"
+          }
+        },
+        "onetime": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/onetime/-/onetime-6.0.0.tgz",
+          "integrity": "sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==",
+          "dev": true,
+          "requires": {
+            "mimic-fn": "^4.0.0"
+          }
+        },
+        "path-key": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/path-key/-/path-key-4.0.0.tgz",
+          "integrity": "sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==",
+          "dev": true
+        },
+        "strip-final-newline": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-3.0.0.tgz",
+          "integrity": "sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==",
+          "dev": true
+        }
+      }
+    },
+    "listr2": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/listr2/-/listr2-5.0.8.tgz",
+      "integrity": "sha512-mC73LitKHj9w6v30nLNGPetZIlfpUniNSsxxrbaPcWOjDb92SHPzJPi/t+v1YC/lxKz/AJ9egOjww0qUuFxBpA==",
+      "dev": true,
+      "requires": {
+        "cli-truncate": "^2.1.0",
+        "colorette": "^2.0.19",
+        "log-update": "^4.0.0",
+        "p-map": "^4.0.0",
+        "rfdc": "^1.3.0",
+        "rxjs": "^7.8.0",
+        "through": "^2.3.8",
+        "wrap-ansi": "^7.0.0"
+      },
+      "dependencies": {
+        "cli-truncate": {
+          "version": "2.1.0",
+          "resolved": "https://registry.npmjs.org/cli-truncate/-/cli-truncate-2.1.0.tgz",
+          "integrity": "sha512-n8fOixwDD6b/ObinzTrp1ZKFzbgvKZvuz/TvejnLn1aQfC6r52XEx85FmuC+3HI+JM7coBRXUvNqEU2PHVrHpg==",
+          "dev": true,
+          "requires": {
+            "slice-ansi": "^3.0.0",
+            "string-width": "^4.2.0"
+          }
+        },
+        "p-map": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/p-map/-/p-map-4.0.0.tgz",
+          "integrity": "sha512-/bjOqmgETBYB5BoEeGVea8dmvHb2m9GLy1E9W43yeyfP6QQCZGFNa+XRceJEuDB6zqr+gKpIAmlLebMpykw/MQ==",
+          "dev": true,
+          "requires": {
+            "aggregate-error": "^3.0.0"
+          }
+        },
+        "slice-ansi": {
+          "version": "3.0.0",
+          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-3.0.0.tgz",
+          "integrity": "sha512-pSyv7bSTC7ig9Dcgbw9AuRNUb5k5V6oDudjZoMBSr13qpLBG7tB+zgCkARjq7xIUgdz5P1Qe8u+rSGdouOOIyQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "astral-regex": "^2.0.0",
+            "is-fullwidth-code-point": "^3.0.0"
+          }
+        }
+      }
+    },
     "locate-path": {
       "version": "6.0.0",
       "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz",
@@ -12591,6 +13267,42 @@
         "is-unicode-supported": "^0.1.0"
       }
     },
+    "log-update": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/log-update/-/log-update-4.0.0.tgz",
+      "integrity": "sha512-9fkkDevMefjg0mmzWFBW8YkFP91OrizzkW3diF7CpG+S2EYdy4+TVfGwz1zeF8x7hCx1ovSPTOE9Ngib74qqUg==",
+      "dev": true,
+      "requires": {
+        "ansi-escapes": "^4.3.0",
+        "cli-cursor": "^3.1.0",
+        "slice-ansi": "^4.0.0",
+        "wrap-ansi": "^6.2.0"
+      },
+      "dependencies": {
+        "slice-ansi": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz",
+          "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "astral-regex": "^2.0.0",
+            "is-fullwidth-code-point": "^3.0.0"
+          }
+        },
+        "wrap-ansi": {
+          "version": "6.2.0",
+          "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-6.2.0.tgz",
+          "integrity": "sha512-r6lPcBGxZXlIcymEu7InxDMhdW0KDxpLgoFLcguasxCaJ/SOIZwINatK9KY/tf+ZrlywOKU0UDj3ATXUBfxJXA==",
+          "dev": true,
+          "requires": {
+            "ansi-styles": "^4.0.0",
+            "string-width": "^4.1.0",
+            "strip-ansi": "^6.0.0"
+          }
+        }
+      }
+    },
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -13667,6 +14379,12 @@
       "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz",
       "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA=="
     },
+    "pidtree": {
+      "version": "0.6.0",
+      "resolved": "https://registry.npmjs.org/pidtree/-/pidtree-0.6.0.tgz",
+      "integrity": "sha512-eG2dWTVw5bzqGRztnHExczNxt5VGsE6OwTeCG3fdUf9KBsZzO3R5OIIIzWR+iZA0NtZ+RDVdaoE2dK1cn6jH4g==",
+      "dev": true
+    },
     "piscina": {
       "version": "3.2.0",
       "resolved": "https://registry.npmjs.org/piscina/-/piscina-3.2.0.tgz",
@@ -14193,6 +14911,12 @@
       "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==",
       "dev": true
     },
+    "rfdc": {
+      "version": "1.3.0",
+      "resolved": "https://registry.npmjs.org/rfdc/-/rfdc-1.3.0.tgz",
+      "integrity": "sha512-V2hovdzFbOi77/WajaSMXk2OLm+xNIeQdMMuB7icj7bk6zi2F8GGAxigcnDFpJHbNyNcgyJDiP+8nOrY5cZGrA==",
+      "dev": true
+    },
     "rimraf": {
       "version": "3.0.2",
       "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz",
@@ -14525,6 +15249,30 @@
       "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==",
       "dev": true
     },
+    "slice-ansi": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-5.0.0.tgz",
+      "integrity": "sha512-FC+lgizVPfie0kkhqUScwRu1O/lF6NOgJmlCgK+/LYxDCTk8sGelYaHDhFcDN+Sn3Cv+3VSa4Byeo+IMCzpMgQ==",
+      "dev": true,
+      "requires": {
+        "ansi-styles": "^6.0.0",
+        "is-fullwidth-code-point": "^4.0.0"
+      },
+      "dependencies": {
+        "ansi-styles": {
+          "version": "6.2.1",
+          "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz",
+          "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==",
+          "dev": true
+        },
+        "is-fullwidth-code-point": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-4.0.0.tgz",
+          "integrity": "sha512-O4L094N2/dZ7xqVdrXhh9r1KODPJpFms8B5sGdJLPy664AgvXsreZUyCQQNItZRDlYug4xStLjNp/sz3HvBowQ==",
+          "dev": true
+        }
+      }
+    },
     "source-map": {
       "version": "0.6.1",
       "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
@@ -14795,6 +15543,12 @@
         "thenify": ">= 3.1.0 < 4"
       }
     },
+    "through": {
+      "version": "2.3.8",
+      "resolved": "https://registry.npmjs.org/through/-/through-2.3.8.tgz",
+      "integrity": "sha512-w89qg7PI8wAdvX60bMDP+bFoD5Dvhm9oLheFp5O4a2QF0cSBGsBX4qZmadPMvVqlLJBBci+WqGGOAPvcDeNSVg==",
+      "dev": true
+    },
     "tmp": {
       "version": "0.2.1",
       "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.1.tgz",
@@ -15292,6 +16046,12 @@
       "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz",
       "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A=="
     },
+    "yaml": {
+      "version": "2.2.2",
+      "resolved": "https://registry.npmjs.org/yaml/-/yaml-2.2.2.tgz",
+      "integrity": "sha512-CBKFWExMn46Foo4cldiChEzn7S7SRV+wqiluAb6xmueD/fGyRHIhX8m14vVGgeFWjN540nKCNVj6P21eQjgTuA==",
+      "dev": true
+    },
     "yargs": {
       "version": "16.2.0",
       "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz",
diff --git a/package.json b/package.json
index e1275fbc..6f2351a9 100644
--- a/package.json
+++ b/package.json
@@ -59,8 +59,10 @@
     "@types/node": "^18.11.7",
     "coveralls": "^3.1.1",
     "eslint": "^8.9.0",
+    "husky": "^8.0.0",
     "is-ci": "^3.0.1",
     "js-yaml": "^3.13.1",
+    "lint-staged": "^13.2.2",
     "lodash": "^4.17.21",
     "mocha-junit-reporter": "^2.0.2",
     "mochawesome": "^7.1.3",
@@ -103,7 +105,8 @@
     "test:types": "tsd --files types/tests",
     "component-tests": "mocha test/component-tests/src",
     "test-cucumber": "mocha test/cucumber-integration-tests --parallel",
-    "coverage": "npx nyc report --reporter=text-lcov > ./coverage/mocha_coverage.lcov"
+    "coverage": "npx nyc report --reporter=text-lcov > ./coverage/mocha_coverage.lcov",
+    "prepare": "husky install"
   },
   "eslintConfig": {
     "extends": "eslint:recommended",
@@ -158,5 +161,10 @@
     "CODE_OF_CONDUCT.md",
     "LICENSE.md",
     "index.js"
-  ]
+  ],
+  "lint-staged":{
+    "**/*.js":[
+      "npx eslint --fix"
+    ]
+  }
 }


Test patch:
diff --git a/test/apidemos/web-elements/assertionTest.js b/test/apidemos/web-elements/assertionTest.js
new file mode 100644
index 00000000..35d9fc8d
--- /dev/null
+++ b/test/apidemos/web-elements/assertionTest.js
@@ -0,0 +1,15 @@
+describe('element assertion test', function() {
+  it('element is visible', function({element}) {
+    element.find('#weblogin').assert.visible();
+    element.find('#weblogin').assert.enabled();
+  });
+
+  it('element is not visible - failure', function({element}) {
+    element.find('#weblogin').assert.not.visible();
+  });
+
+  it('element is present', function({element}){
+    element.find('#weblogin').assert.present();
+  });
+
+});
\ No newline at end of file
diff --git a/test/src/api/commands/web-element/assert/testElementAssertions.js b/test/src/api/commands/web-element/assert/testElementAssertions.js
index 2287b030..868d9621 100644
--- a/test/src/api/commands/web-element/assert/testElementAssertions.js
+++ b/test/src/api/commands/web-element/assert/testElementAssertions.js
@@ -6,7 +6,10 @@ const Element = require('../../../../../../lib/element/index.js');
 describe('element() assert commands', function () {
   this.timeout(10000);
   before(function (done) {
-    CommandGlobals.beforeEach.call(this, done);
+    CommandGlobals.beforeEach.call(this, () => {
+      this.client.isES6AsyncTestcase = true;
+      done();
+    });
   });
 
   after(function (done) {
@@ -153,40 +156,40 @@ describe('element() assert commands', function () {
     }
   });
 
-  // it('test .element() present assert', async function() {
-  //   MockServer.addMock({
-  //     url: '/session/13521-10219-202/elements',
-  //     method: 'POST',
-  //     response: JSON.stringify({
-  //       value: true
-  //     })
-  //   }, true, true);
-
-  //   this.client.options.globals.retryAssertionTimeout = 0;
-
-  //   const signupElement = this.client.api.element('#signupSection');
-  //   const assertPromise = signupElement.assert.present();
-  //   const notAssertPromise = signupElement.assert.not.present();
-
-  //   assert.strictEqual(assertPromise instanceof Element, false);
-  //   assert.strictEqual(assertPromise instanceof Promise, true);
-  //   assert.strictEqual(typeof assertPromise.find, 'undefined');
-
-  //   await assertPromise;
-  //   try {
-  //     await notAssertPromise;
-  //     assert.fail('NightwatchAssertError expected');
-  //   } catch (err) {
-  //     assert.strictEqual(err.name, 'NightwatchAssertError');
-  //   }
-  // });
+  it('test .element() present assert', async function() {
+    MockServer.addMock({
+      url: '/session/13521-10219-202/elements',
+      method: 'POST',
+      response: JSON.stringify({
+        value: true
+      })
+    }, true, true);
+
+    this.client.options.globals.retryAssertionTimeout = 0;
+
+    const signupElement = this.client.api.element('#signupSection');
+    const assertPromise = signupElement.assert.present();
+    const notAssertPromise = signupElement.assert.not.present();
+
+    assert.strictEqual(assertPromise instanceof Element, false);
+    assert.strictEqual(assertPromise instanceof Promise, true);
+    assert.strictEqual(typeof assertPromise.find, 'undefined');
+
+    await assertPromise;
+    try {
+      await notAssertPromise;
+      assert.fail('NightwatchAssertError expected');
+    } catch (err) {
+      assert.strictEqual(err.name, 'NightwatchAssertError');
+    }
+  });
 
   it('test .element() hasClass assert', async function() {
     MockServer.addMock({
       url: '/session/13521-10219-202/execute/sync',
       method: 'POST',
       response: JSON.stringify({
-        value: ['section', 'signup']
+        value: true
       })
     }, true, true);
 
@@ -194,7 +197,6 @@ describe('element() assert commands', function () {
 
     const signupElement = this.client.api.element('#signupSection');
     const assertPromise = signupElement.assert.hasClass('signup');
-    const failAssertPromise = signupElement.assert.hasClass('signupx');
     const notAssertPromise = signupElement.assert.not.hasClass('signup');
 
     assert.strictEqual(assertPromise instanceof Element, false);
@@ -203,14 +205,12 @@ describe('element() assert commands', function () {
 
     await assertPromise;
 
-    // TODO: Fix this. Below assertion should throw error, but instead fail silently
-    await failAssertPromise;
-    // try {
-    //   await notAssertPromise;
-    //   assert.fail('AssertionError expected');
-    // } catch (err) {
-    //   assert.strictEqual(err.name, 'NightwatchAssertError');
-    // }
+    try {
+      await notAssertPromise;
+      assert.fail('AssertionError expected');
+    } catch (err) {
+      assert.strictEqual(err.name, 'NightwatchAssertError');
+    }
   });
 
   it('test .element() hasAttribute assert', async function() {
@@ -234,13 +234,11 @@ describe('element() assert commands', function () {
 
     await assertPromise;
 
-    // TODO: Fix this. Below assertion should throw error, but instead fail silently
-    await notAssertPromise;
-    // try {
-    //   await notAssertPromise;
-    //   assert.fail('AssertionError expected');
-    // } catch (err) {
-    //   assert.strictEqual(err.name, 'NightwatchAssertError');
-    // }
+    try {
+      await notAssertPromise;
+      assert.fail('AssertionError expected');
+    } catch (err) {
+      assert.strictEqual(err.name, 'NightwatchAssertError');
+    }
   });
-});
+});
\ No newline at end of file
diff --git a/test/src/apidemos/web-elements/testAssert.js b/test/src/apidemos/web-elements/testAssert.js
new file mode 100644
index 00000000..79df0e53
--- /dev/null
+++ b/test/src/apidemos/web-elements/testAssert.js
@@ -0,0 +1,55 @@
+const path = require('path');
+const assert = require('assert');
+
+const commandMocks = require('../../../lib/command-mocks');
+const common = require('../../../common.js');
+const {settings} = common;
+const NightwatchClient = common.require('index.js');
+
+describe('element Assertions', function() {
+  beforeEach(function(done) {
+    commandMocks.start(done);
+  });
+
+  afterEach(function(done) {
+    commandMocks.stop(done);
+  });
+
+  it('test assert element command ', function() {
+    const testsPath = path.join(__dirname, '../../../apidemos/web-elements/assertionTest.js');
+    commandMocks.w3cVisible();
+    commandMocks.w3cEnabled();
+    commandMocks.w3cVisible(true);
+
+    const globals = {
+      waitForConditionPollInterval: 50,
+      waitForConditionTimeout: 120,
+      retryAssertionTimeout: 0,
+
+      reporter(results) {
+        if (!results.lastError) {
+          assert.fail('should have one failure');
+        }
+        assert.strictEqual(results.passed, 3);
+        assert.strictEqual(results.failed, 1);
+        assert.ok(results.lastError.name === 'NightwatchAssertError');
+      }
+    };
+
+    return NightwatchClient.runTests(testsPath, settings({
+      selenium: {
+        port: null,
+        start_process: false
+      },
+      selenium_host: null,
+      webdriver: {
+        port: 10195,
+        start_process: false
+      },
+      output: false,
+      skip_testcases_on_fail: false,
+      globals
+    }));
+  });
+
+});