diff --git a/.github/workflows/commitlint.yml b/.github/workflows/commitlint.yml
new file mode 100644
index 0000000..4a615c8
--- /dev/null
+++ b/.github/workflows/commitlint.yml
@@ -0,0 +1,9 @@
+name: Lint commit messages
+on: [pull_request]
+
+jobs:
+  commitlint:
+    runs-on: ubuntu-latest
+    steps:
+      - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4
+      - uses: wagoid/commitlint-github-action@5ce82f5d814d4010519d15f0552aec4f17a1e1fe # v5
diff --git a/commitlint.config.js b/commitlint.config.js
new file mode 100644
index 0000000..d89185e
--- /dev/null
+++ b/commitlint.config.js
@@ -0,0 +1,4 @@
+module.exports = {
+    extends: ['@commitlint/config-conventional'],
+    ignores: [message => message.startsWith('chore(release):')]
+};
diff --git a/package-lock.json b/package-lock.json
index df97c56..890fea2 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -14,6 +14,8 @@
       },
       "devDependencies": {
         "@babel/eslint-parser": "^7.5.4",
+        "@commitlint/cli": "^19.7.1",
+        "@commitlint/config-conventional": "^19.7.1",
         "eslint": "^8.16.0",
         "eslint-config-scratch": "^7.0.0",
         "scratch-semantic-release-config": "1.0.8",
@@ -359,6 +361,576 @@
         "node": ">=0.1.90"
       }
     },
+    "node_modules/@commitlint/cli": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.7.1.tgz",
+      "integrity": "sha512-iObGjR1tE/PfDtDTEfd+tnRkB3/HJzpQqRTyofS2MPPkDn1mp3DBC8SoPDayokfAy+xKhF8+bwRCJO25Nea0YQ==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/format": "^19.5.0",
+        "@commitlint/lint": "^19.7.1",
+        "@commitlint/load": "^19.6.1",
+        "@commitlint/read": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "tinyexec": "^0.3.0",
+        "yargs": "^17.0.0"
+      },
+      "bin": {
+        "commitlint": "cli.js"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/cli/node_modules/cliui": {
+      "version": "8.0.1",
+      "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+      "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+      "dev": true,
+      "dependencies": {
+        "string-width": "^4.2.0",
+        "strip-ansi": "^6.0.1",
+        "wrap-ansi": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@commitlint/cli/node_modules/y18n": {
+      "version": "5.0.8",
+      "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+      "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+      "dev": true,
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@commitlint/cli/node_modules/yargs": {
+      "version": "17.7.2",
+      "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+      "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+      "dev": true,
+      "dependencies": {
+        "cliui": "^8.0.1",
+        "escalade": "^3.1.1",
+        "get-caller-file": "^2.0.5",
+        "require-directory": "^2.1.1",
+        "string-width": "^4.2.3",
+        "y18n": "^5.0.5",
+        "yargs-parser": "^21.1.1"
+      },
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@commitlint/cli/node_modules/yargs-parser": {
+      "version": "21.1.1",
+      "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+      "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      }
+    },
+    "node_modules/@commitlint/config-conventional": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.7.1.tgz",
+      "integrity": "sha512-fsEIF8zgiI/FIWSnykdQNj/0JE4av08MudLTyYHm4FlLWemKoQvPNUYU2M/3tktWcCEyq7aOkDDgtjrmgWFbvg==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "conventional-changelog-conventionalcommits": "^7.0.2"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/config-validator": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz",
+      "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "ajv": "^8.11.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/config-validator/node_modules/ajv": {
+      "version": "8.17.1",
+      "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+      "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+      "dev": true,
+      "dependencies": {
+        "fast-deep-equal": "^3.1.3",
+        "fast-uri": "^3.0.1",
+        "json-schema-traverse": "^1.0.0",
+        "require-from-string": "^2.0.2"
+      },
+      "funding": {
+        "type": "github",
+        "url": "https://github.com/sponsors/epoberezkin"
+      }
+    },
+    "node_modules/@commitlint/config-validator/node_modules/json-schema-traverse": {
+      "version": "1.0.0",
+      "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+      "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+      "dev": true
+    },
+    "node_modules/@commitlint/ensure": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz",
+      "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.kebabcase": "^4.1.1",
+        "lodash.snakecase": "^4.1.1",
+        "lodash.startcase": "^4.4.0",
+        "lodash.upperfirst": "^4.3.1"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/execute-rule": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz",
+      "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/format": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz",
+      "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "chalk": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/format/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "dev": true,
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@commitlint/is-ignored": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.7.1.tgz",
+      "integrity": "sha512-3IaOc6HVg2hAoGleRK3r9vL9zZ3XY0rf1RsUf6jdQLuaD46ZHnXBiOPTyQ004C4IvYjSWqJwlh0/u2P73aIE3g==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "semver": "^7.6.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/is-ignored/node_modules/semver": {
+      "version": "7.7.1",
+      "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+      "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+      "dev": true,
+      "bin": {
+        "semver": "bin/semver.js"
+      },
+      "engines": {
+        "node": ">=10"
+      }
+    },
+    "node_modules/@commitlint/lint": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.7.1.tgz",
+      "integrity": "sha512-LhcPfVjcOcOZA7LEuBBeO00o3MeZa+tWrX9Xyl1r9PMd5FWsEoZI9IgnGqTKZ0lZt5pO3ZlstgnRyY1CJJc9Xg==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/is-ignored": "^19.7.1",
+        "@commitlint/parse": "^19.5.0",
+        "@commitlint/rules": "^19.6.0",
+        "@commitlint/types": "^19.5.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/load": {
+      "version": "19.6.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.6.1.tgz",
+      "integrity": "sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/config-validator": "^19.5.0",
+        "@commitlint/execute-rule": "^19.5.0",
+        "@commitlint/resolve-extends": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "chalk": "^5.3.0",
+        "cosmiconfig": "^9.0.0",
+        "cosmiconfig-typescript-loader": "^6.1.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "lodash.uniq": "^4.5.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/load/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "dev": true,
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
+    "node_modules/@commitlint/load/node_modules/cosmiconfig": {
+      "version": "9.0.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+      "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+      "dev": true,
+      "dependencies": {
+        "env-paths": "^2.2.1",
+        "import-fresh": "^3.3.0",
+        "js-yaml": "^4.1.0",
+        "parse-json": "^5.2.0"
+      },
+      "engines": {
+        "node": ">=14"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/d-fischer"
+      },
+      "peerDependencies": {
+        "typescript": ">=4.9.5"
+      },
+      "peerDependenciesMeta": {
+        "typescript": {
+          "optional": true
+        }
+      }
+    },
+    "node_modules/@commitlint/load/node_modules/cosmiconfig-typescript-loader": {
+      "version": "6.1.0",
+      "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz",
+      "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==",
+      "dev": true,
+      "dependencies": {
+        "jiti": "^2.4.1"
+      },
+      "engines": {
+        "node": ">=v18"
+      },
+      "peerDependencies": {
+        "@types/node": "*",
+        "cosmiconfig": ">=9",
+        "typescript": ">=5"
+      }
+    },
+    "node_modules/@commitlint/message": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz",
+      "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/parse": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz",
+      "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/types": "^19.5.0",
+        "conventional-changelog-angular": "^7.0.0",
+        "conventional-commits-parser": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/conventional-changelog-angular": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+      "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+      "dev": true,
+      "dependencies": {
+        "compare-func": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/conventional-commits-parser": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+      "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+      "dev": true,
+      "dependencies": {
+        "is-text-path": "^2.0.0",
+        "JSONStream": "^1.3.5",
+        "meow": "^12.0.1",
+        "split2": "^4.0.0"
+      },
+      "bin": {
+        "conventional-commits-parser": "cli.mjs"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/is-text-path": {
+      "version": "2.0.0",
+      "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+      "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+      "dev": true,
+      "dependencies": {
+        "text-extensions": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/meow": {
+      "version": "12.1.1",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+      "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
+    "node_modules/@commitlint/parse/node_modules/text-extensions": {
+      "version": "2.4.0",
+      "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+      "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/read": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz",
+      "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/top-level": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "git-raw-commits": "^4.0.0",
+        "minimist": "^1.2.8",
+        "tinyexec": "^0.3.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/resolve-extends": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz",
+      "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/config-validator": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "global-directory": "^4.0.1",
+        "import-meta-resolve": "^4.0.0",
+        "lodash.mergewith": "^4.6.2",
+        "resolve-from": "^5.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/resolve-extends/node_modules/resolve-from": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+      "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+      "dev": true,
+      "engines": {
+        "node": ">=8"
+      }
+    },
+    "node_modules/@commitlint/rules": {
+      "version": "19.6.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.6.0.tgz",
+      "integrity": "sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==",
+      "dev": true,
+      "dependencies": {
+        "@commitlint/ensure": "^19.5.0",
+        "@commitlint/message": "^19.5.0",
+        "@commitlint/to-lines": "^19.5.0",
+        "@commitlint/types": "^19.5.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/to-lines": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz",
+      "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==",
+      "dev": true,
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/top-level": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz",
+      "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==",
+      "dev": true,
+      "dependencies": {
+        "find-up": "^7.0.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/find-up": {
+      "version": "7.0.0",
+      "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
+      "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==",
+      "dev": true,
+      "dependencies": {
+        "locate-path": "^7.2.0",
+        "path-exists": "^5.0.0",
+        "unicorn-magic": "^0.1.0"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/locate-path": {
+      "version": "7.2.0",
+      "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+      "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+      "dev": true,
+      "dependencies": {
+        "p-locate": "^6.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/p-limit": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+      "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+      "dev": true,
+      "dependencies": {
+        "yocto-queue": "^1.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/p-locate": {
+      "version": "6.0.0",
+      "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+      "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+      "dev": true,
+      "dependencies": {
+        "p-limit": "^4.0.0"
+      },
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/path-exists": {
+      "version": "5.0.0",
+      "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+      "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+      "dev": true,
+      "engines": {
+        "node": "^12.20.0 || ^14.13.1 || >=16.0.0"
+      }
+    },
+    "node_modules/@commitlint/top-level/node_modules/yocto-queue": {
+      "version": "1.1.1",
+      "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz",
+      "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==",
+      "dev": true,
+      "engines": {
+        "node": ">=12.20"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/@commitlint/types": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz",
+      "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==",
+      "dev": true,
+      "dependencies": {
+        "@types/conventional-commits-parser": "^5.0.0",
+        "chalk": "^5.3.0"
+      },
+      "engines": {
+        "node": ">=v18"
+      }
+    },
+    "node_modules/@commitlint/types/node_modules/chalk": {
+      "version": "5.4.1",
+      "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+      "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+      "dev": true,
+      "engines": {
+        "node": "^12.17.0 || ^14.13 || >=16.0.0"
+      },
+      "funding": {
+        "url": "https://github.com/chalk/chalk?sponsor=1"
+      }
+    },
     "node_modules/@eslint-community/eslint-utils": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -1094,12 +1666,30 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/@types/conventional-commits-parser": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz",
+      "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==",
+      "dev": true,
+      "dependencies": {
+        "@types/node": "*"
+      }
+    },
     "node_modules/@types/minimist": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
       "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
       "dev": true
     },
+    "node_modules/@types/node": {
+      "version": "22.13.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
+      "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
+      "dev": true,
+      "dependencies": {
+        "undici-types": "~6.20.0"
+      }
+    },
     "node_modules/@types/normalize-package-data": {
       "version": "2.4.4",
       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
@@ -1834,6 +2424,18 @@
         "node": ">=10"
       }
     },
+    "node_modules/conventional-changelog-conventionalcommits": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+      "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+      "dev": true,
+      "dependencies": {
+        "compare-func": "^2.0.0"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
     "node_modules/conventional-changelog-writer": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz",
@@ -1957,6 +2559,18 @@
         "node": ">=8"
       }
     },
+    "node_modules/dargs": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz",
+      "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==",
+      "dev": true,
+      "engines": {
+        "node": ">=12"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
     "node_modules/dateformat": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@@ -2355,6 +2969,15 @@
         "url": "https://github.com/sponsors/sindresorhus"
       }
     },
+    "node_modules/env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true,
+      "engines": {
+        "node": ">=6"
+      }
+    },
     "node_modules/error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -2969,6 +3592,22 @@
       "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
       "dev": true
     },
+    "node_modules/fast-uri": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+      "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+      "dev": true,
+      "funding": [
+        {
+          "type": "github",
+          "url": "https://github.com/sponsors/fastify"
+        },
+        {
+          "type": "opencollective",
+          "url": "https://opencollective.com/fastify"
+        }
+      ]
+    },
     "node_modules/fastq": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -3391,6 +4030,44 @@
         "xtend": "~4.0.1"
       }
     },
+    "node_modules/git-raw-commits": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
+      "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==",
+      "dev": true,
+      "dependencies": {
+        "dargs": "^8.0.0",
+        "meow": "^12.0.1",
+        "split2": "^4.0.0"
+      },
+      "bin": {
+        "git-raw-commits": "cli.mjs"
+      },
+      "engines": {
+        "node": ">=16"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/meow": {
+      "version": "12.1.1",
+      "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+      "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+      "dev": true,
+      "engines": {
+        "node": ">=16.10"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/git-raw-commits/node_modules/split2": {
+      "version": "4.2.0",
+      "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+      "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+      "dev": true,
+      "engines": {
+        "node": ">= 10.x"
+      }
+    },
     "node_modules/glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -3423,6 +4100,30 @@
         "node": ">=10.13.0"
       }
     },
+    "node_modules/global-directory": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+      "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+      "dev": true,
+      "dependencies": {
+        "ini": "4.1.1"
+      },
+      "engines": {
+        "node": ">=18"
+      },
+      "funding": {
+        "url": "https://github.com/sponsors/sindresorhus"
+      }
+    },
+    "node_modules/global-directory/node_modules/ini": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+      "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+      "dev": true,
+      "engines": {
+        "node": "^14.17.0 || ^16.13.0 || >=18.0.0"
+      }
+    },
     "node_modules/globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -3770,7 +4471,6 @@
       "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
       "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==",
       "dev": true,
-      "peer": true,
       "funding": {
         "type": "github",
         "url": "https://github.com/sponsors/wooorm"
@@ -4536,6 +5236,15 @@
         "node": ">= 0.6.0"
       }
     },
+    "node_modules/jiti": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+      "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+      "dev": true,
+      "bin": {
+        "jiti": "lib/jiti-cli.mjs"
+      }
+    },
     "node_modules/js-md5": {
       "version": "0.7.3",
       "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
@@ -4836,6 +5545,12 @@
       "dev": true,
       "peer": true
     },
+    "node_modules/lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "dev": true
+    },
     "node_modules/lodash.capitalize": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
@@ -4872,18 +5587,54 @@
       "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
       "dev": true
     },
+    "node_modules/lodash.kebabcase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+      "dev": true
+    },
     "node_modules/lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "node_modules/lodash.mergewith": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+      "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+      "dev": true
+    },
+    "node_modules/lodash.snakecase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+      "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+      "dev": true
+    },
+    "node_modules/lodash.startcase": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+      "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+      "dev": true
+    },
+    "node_modules/lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+      "dev": true
+    },
     "node_modules/lodash.uniqby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
       "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
       "dev": true
     },
+    "node_modules/lodash.upperfirst": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+      "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+      "dev": true
+    },
     "node_modules/loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -8925,6 +9676,15 @@
         "node": ">=0.10.0"
       }
     },
+    "node_modules/require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true,
+      "engines": {
+        "node": ">=0.10.0"
+      }
+    },
     "node_modules/require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
@@ -16196,6 +16956,12 @@
         "safe-buffer": "~5.2.0"
       }
     },
+    "node_modules/tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true
+    },
     "node_modules/to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -16350,6 +17116,20 @@
         "is-typedarray": "^1.0.0"
       }
     },
+    "node_modules/typescript": {
+      "version": "5.7.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+      "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+      "dev": true,
+      "peer": true,
+      "bin": {
+        "tsc": "bin/tsc",
+        "tsserver": "bin/tsserver"
+      },
+      "engines": {
+        "node": ">=14.17"
+      }
+    },
     "node_modules/uglify-js": {
       "version": "3.17.4",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
@@ -16379,6 +17159,12 @@
         "url": "https://github.com/sponsors/ljharb"
       }
     },
+    "node_modules/undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "dev": true
+    },
     "node_modules/unicode-emoji-modifier-base": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
@@ -16403,7 +17189,6 @@
       "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
       "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
       "dev": true,
-      "peer": true,
       "engines": {
         "node": ">=18"
       },
@@ -17138,6 +17923,415 @@
       "optional": true,
       "peer": true
     },
+    "@commitlint/cli": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/cli/-/cli-19.7.1.tgz",
+      "integrity": "sha512-iObGjR1tE/PfDtDTEfd+tnRkB3/HJzpQqRTyofS2MPPkDn1mp3DBC8SoPDayokfAy+xKhF8+bwRCJO25Nea0YQ==",
+      "dev": true,
+      "requires": {
+        "@commitlint/format": "^19.5.0",
+        "@commitlint/lint": "^19.7.1",
+        "@commitlint/load": "^19.6.1",
+        "@commitlint/read": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "tinyexec": "^0.3.0",
+        "yargs": "^17.0.0"
+      },
+      "dependencies": {
+        "cliui": {
+          "version": "8.0.1",
+          "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz",
+          "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==",
+          "dev": true,
+          "requires": {
+            "string-width": "^4.2.0",
+            "strip-ansi": "^6.0.1",
+            "wrap-ansi": "^7.0.0"
+          }
+        },
+        "y18n": {
+          "version": "5.0.8",
+          "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz",
+          "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==",
+          "dev": true
+        },
+        "yargs": {
+          "version": "17.7.2",
+          "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz",
+          "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==",
+          "dev": true,
+          "requires": {
+            "cliui": "^8.0.1",
+            "escalade": "^3.1.1",
+            "get-caller-file": "^2.0.5",
+            "require-directory": "^2.1.1",
+            "string-width": "^4.2.3",
+            "y18n": "^5.0.5",
+            "yargs-parser": "^21.1.1"
+          }
+        },
+        "yargs-parser": {
+          "version": "21.1.1",
+          "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz",
+          "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/config-conventional": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/config-conventional/-/config-conventional-19.7.1.tgz",
+      "integrity": "sha512-fsEIF8zgiI/FIWSnykdQNj/0JE4av08MudLTyYHm4FlLWemKoQvPNUYU2M/3tktWcCEyq7aOkDDgtjrmgWFbvg==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "conventional-changelog-conventionalcommits": "^7.0.2"
+      }
+    },
+    "@commitlint/config-validator": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/config-validator/-/config-validator-19.5.0.tgz",
+      "integrity": "sha512-CHtj92H5rdhKt17RmgALhfQt95VayrUo2tSqY9g2w+laAXyk7K/Ef6uPm9tn5qSIwSmrLjKaXK9eiNuxmQrDBw==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "ajv": "^8.11.0"
+      },
+      "dependencies": {
+        "ajv": {
+          "version": "8.17.1",
+          "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.17.1.tgz",
+          "integrity": "sha512-B/gBuNg5SiMTrPkC+A2+cW0RszwxYmn6VYxB/inlBStS5nx6xHIt/ehKRhIMhqusl7a8LjQoZnjCs5vhwxOQ1g==",
+          "dev": true,
+          "requires": {
+            "fast-deep-equal": "^3.1.3",
+            "fast-uri": "^3.0.1",
+            "json-schema-traverse": "^1.0.0",
+            "require-from-string": "^2.0.2"
+          }
+        },
+        "json-schema-traverse": {
+          "version": "1.0.0",
+          "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz",
+          "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/ensure": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/ensure/-/ensure-19.5.0.tgz",
+      "integrity": "sha512-Kv0pYZeMrdg48bHFEU5KKcccRfKmISSm9MvgIgkpI6m+ohFTB55qZlBW6eYqh/XDfRuIO0x4zSmvBjmOwWTwkg==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "lodash.camelcase": "^4.3.0",
+        "lodash.kebabcase": "^4.1.1",
+        "lodash.snakecase": "^4.1.1",
+        "lodash.startcase": "^4.4.0",
+        "lodash.upperfirst": "^4.3.1"
+      }
+    },
+    "@commitlint/execute-rule": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/execute-rule/-/execute-rule-19.5.0.tgz",
+      "integrity": "sha512-aqyGgytXhl2ejlk+/rfgtwpPexYyri4t8/n4ku6rRJoRhGZpLFMqrZ+YaubeGysCP6oz4mMA34YSTaSOKEeNrg==",
+      "dev": true
+    },
+    "@commitlint/format": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/format/-/format-19.5.0.tgz",
+      "integrity": "sha512-yNy088miE52stCI3dhG/vvxFo9e4jFkU1Mj3xECfzp/bIS/JUay4491huAlVcffOoMK1cd296q0W92NlER6r3A==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "chalk": "^5.3.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "5.4.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+          "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/is-ignored": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/is-ignored/-/is-ignored-19.7.1.tgz",
+      "integrity": "sha512-3IaOc6HVg2hAoGleRK3r9vL9zZ3XY0rf1RsUf6jdQLuaD46ZHnXBiOPTyQ004C4IvYjSWqJwlh0/u2P73aIE3g==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "semver": "^7.6.0"
+      },
+      "dependencies": {
+        "semver": {
+          "version": "7.7.1",
+          "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.1.tgz",
+          "integrity": "sha512-hlq8tAfn0m/61p4BVRcPzIGr6LKiMwo4VM6dGi6pt4qcRkmNzTcWq6eCEjEh+qXjkMDvPlOFFSGwQjoEa6gyMA==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/lint": {
+      "version": "19.7.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/lint/-/lint-19.7.1.tgz",
+      "integrity": "sha512-LhcPfVjcOcOZA7LEuBBeO00o3MeZa+tWrX9Xyl1r9PMd5FWsEoZI9IgnGqTKZ0lZt5pO3ZlstgnRyY1CJJc9Xg==",
+      "dev": true,
+      "requires": {
+        "@commitlint/is-ignored": "^19.7.1",
+        "@commitlint/parse": "^19.5.0",
+        "@commitlint/rules": "^19.6.0",
+        "@commitlint/types": "^19.5.0"
+      }
+    },
+    "@commitlint/load": {
+      "version": "19.6.1",
+      "resolved": "https://registry.npmjs.org/@commitlint/load/-/load-19.6.1.tgz",
+      "integrity": "sha512-kE4mRKWWNju2QpsCWt428XBvUH55OET2N4QKQ0bF85qS/XbsRGG1MiTByDNlEVpEPceMkDr46LNH95DtRwcsfA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/config-validator": "^19.5.0",
+        "@commitlint/execute-rule": "^19.5.0",
+        "@commitlint/resolve-extends": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "chalk": "^5.3.0",
+        "cosmiconfig": "^9.0.0",
+        "cosmiconfig-typescript-loader": "^6.1.0",
+        "lodash.isplainobject": "^4.0.6",
+        "lodash.merge": "^4.6.2",
+        "lodash.uniq": "^4.5.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "5.4.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+          "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+          "dev": true
+        },
+        "cosmiconfig": {
+          "version": "9.0.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig/-/cosmiconfig-9.0.0.tgz",
+          "integrity": "sha512-itvL5h8RETACmOTFc4UfIyB2RfEHi71Ax6E/PivVxq9NseKbOWpeyHEOIbmAw1rs8Ak0VursQNww7lf7YtUwzg==",
+          "dev": true,
+          "requires": {
+            "env-paths": "^2.2.1",
+            "import-fresh": "^3.3.0",
+            "js-yaml": "^4.1.0",
+            "parse-json": "^5.2.0"
+          }
+        },
+        "cosmiconfig-typescript-loader": {
+          "version": "6.1.0",
+          "resolved": "https://registry.npmjs.org/cosmiconfig-typescript-loader/-/cosmiconfig-typescript-loader-6.1.0.tgz",
+          "integrity": "sha512-tJ1w35ZRUiM5FeTzT7DtYWAFFv37ZLqSRkGi2oeCK1gPhvaWjkAtfXvLmvE1pRfxxp9aQo6ba/Pvg1dKj05D4g==",
+          "dev": true,
+          "requires": {
+            "jiti": "^2.4.1"
+          }
+        }
+      }
+    },
+    "@commitlint/message": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/message/-/message-19.5.0.tgz",
+      "integrity": "sha512-R7AM4YnbxN1Joj1tMfCyBryOC5aNJBdxadTZkuqtWi3Xj0kMdutq16XQwuoGbIzL2Pk62TALV1fZDCv36+JhTQ==",
+      "dev": true
+    },
+    "@commitlint/parse": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/parse/-/parse-19.5.0.tgz",
+      "integrity": "sha512-cZ/IxfAlfWYhAQV0TwcbdR1Oc0/r0Ik1GEessDJ3Lbuma/MRO8FRQX76eurcXtmhJC//rj52ZSZuXUg0oIX0Fw==",
+      "dev": true,
+      "requires": {
+        "@commitlint/types": "^19.5.0",
+        "conventional-changelog-angular": "^7.0.0",
+        "conventional-commits-parser": "^5.0.0"
+      },
+      "dependencies": {
+        "conventional-changelog-angular": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/conventional-changelog-angular/-/conventional-changelog-angular-7.0.0.tgz",
+          "integrity": "sha512-ROjNchA9LgfNMTTFSIWPzebCwOGFdgkEq45EnvvrmSLvCtAw0HSmrCs7/ty+wAeYUZyNay0YMUNYFTRL72PkBQ==",
+          "dev": true,
+          "requires": {
+            "compare-func": "^2.0.0"
+          }
+        },
+        "conventional-commits-parser": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/conventional-commits-parser/-/conventional-commits-parser-5.0.0.tgz",
+          "integrity": "sha512-ZPMl0ZJbw74iS9LuX9YIAiW8pfM5p3yh2o/NbXHbkFuZzY5jvdi5jFycEOkmBW5H5I7nA+D6f3UcsCLP2vvSEA==",
+          "dev": true,
+          "requires": {
+            "is-text-path": "^2.0.0",
+            "JSONStream": "^1.3.5",
+            "meow": "^12.0.1",
+            "split2": "^4.0.0"
+          }
+        },
+        "is-text-path": {
+          "version": "2.0.0",
+          "resolved": "https://registry.npmjs.org/is-text-path/-/is-text-path-2.0.0.tgz",
+          "integrity": "sha512-+oDTluR6WEjdXEJMnC2z6A4FRwFoYuvShVVEGsS7ewc0UTi2QtAKMDJuL4BDEVt+5T7MjFo12RP8ghOM75oKJw==",
+          "dev": true,
+          "requires": {
+            "text-extensions": "^2.0.0"
+          }
+        },
+        "meow": {
+          "version": "12.1.1",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+          "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+          "dev": true
+        },
+        "split2": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+          "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+          "dev": true
+        },
+        "text-extensions": {
+          "version": "2.4.0",
+          "resolved": "https://registry.npmjs.org/text-extensions/-/text-extensions-2.4.0.tgz",
+          "integrity": "sha512-te/NtwBwfiNRLf9Ijqx3T0nlqZiQ2XrrtBvu+cLL8ZRrGkO0NHTug8MYFKyoSrv/sHTaSKfilUkizV6XhxMJ3g==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/read": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/read/-/read-19.5.0.tgz",
+      "integrity": "sha512-TjS3HLPsLsxFPQj6jou8/CZFAmOP2y+6V4PGYt3ihbQKTY1Jnv0QG28WRKl/d1ha6zLODPZqsxLEov52dhR9BQ==",
+      "dev": true,
+      "requires": {
+        "@commitlint/top-level": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "git-raw-commits": "^4.0.0",
+        "minimist": "^1.2.8",
+        "tinyexec": "^0.3.0"
+      }
+    },
+    "@commitlint/resolve-extends": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/resolve-extends/-/resolve-extends-19.5.0.tgz",
+      "integrity": "sha512-CU/GscZhCUsJwcKTJS9Ndh3AKGZTNFIOoQB2n8CmFnizE0VnEuJoum+COW+C1lNABEeqk6ssfc1Kkalm4bDklA==",
+      "dev": true,
+      "requires": {
+        "@commitlint/config-validator": "^19.5.0",
+        "@commitlint/types": "^19.5.0",
+        "global-directory": "^4.0.1",
+        "import-meta-resolve": "^4.0.0",
+        "lodash.mergewith": "^4.6.2",
+        "resolve-from": "^5.0.0"
+      },
+      "dependencies": {
+        "resolve-from": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz",
+          "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/rules": {
+      "version": "19.6.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/rules/-/rules-19.6.0.tgz",
+      "integrity": "sha512-1f2reW7lbrI0X0ozZMesS/WZxgPa4/wi56vFuJENBmed6mWq5KsheN/nxqnl/C23ioxpPO/PL6tXpiiFy5Bhjw==",
+      "dev": true,
+      "requires": {
+        "@commitlint/ensure": "^19.5.0",
+        "@commitlint/message": "^19.5.0",
+        "@commitlint/to-lines": "^19.5.0",
+        "@commitlint/types": "^19.5.0"
+      }
+    },
+    "@commitlint/to-lines": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/to-lines/-/to-lines-19.5.0.tgz",
+      "integrity": "sha512-R772oj3NHPkodOSRZ9bBVNq224DOxQtNef5Pl8l2M8ZnkkzQfeSTr4uxawV2Sd3ui05dUVzvLNnzenDBO1KBeQ==",
+      "dev": true
+    },
+    "@commitlint/top-level": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/top-level/-/top-level-19.5.0.tgz",
+      "integrity": "sha512-IP1YLmGAk0yWrImPRRc578I3dDUI5A2UBJx9FbSOjxe9sTlzFiwVJ+zeMLgAtHMtGZsC8LUnzmW1qRemkFU4ng==",
+      "dev": true,
+      "requires": {
+        "find-up": "^7.0.0"
+      },
+      "dependencies": {
+        "find-up": {
+          "version": "7.0.0",
+          "resolved": "https://registry.npmjs.org/find-up/-/find-up-7.0.0.tgz",
+          "integrity": "sha512-YyZM99iHrqLKjmt4LJDj58KI+fYyufRLBSYcqycxf//KpBk9FoewoGX0450m9nB44qrZnovzC2oeP5hUibxc/g==",
+          "dev": true,
+          "requires": {
+            "locate-path": "^7.2.0",
+            "path-exists": "^5.0.0",
+            "unicorn-magic": "^0.1.0"
+          }
+        },
+        "locate-path": {
+          "version": "7.2.0",
+          "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-7.2.0.tgz",
+          "integrity": "sha512-gvVijfZvn7R+2qyPX8mAuKcFGDf6Nc61GdvGafQsHL0sBIxfKzA+usWn4GFC/bk+QdwPUD4kWFJLhElipq+0VA==",
+          "dev": true,
+          "requires": {
+            "p-locate": "^6.0.0"
+          }
+        },
+        "p-limit": {
+          "version": "4.0.0",
+          "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-4.0.0.tgz",
+          "integrity": "sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==",
+          "dev": true,
+          "requires": {
+            "yocto-queue": "^1.0.0"
+          }
+        },
+        "p-locate": {
+          "version": "6.0.0",
+          "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-6.0.0.tgz",
+          "integrity": "sha512-wPrq66Llhl7/4AGC6I+cqxT07LhXvWL08LNXz1fENOw0Ap4sRZZ/gZpTTJ5jpurzzzfS2W/Ge9BY3LgLjCShcw==",
+          "dev": true,
+          "requires": {
+            "p-limit": "^4.0.0"
+          }
+        },
+        "path-exists": {
+          "version": "5.0.0",
+          "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-5.0.0.tgz",
+          "integrity": "sha512-RjhtfwJOxzcFmNOi6ltcbcu4Iu+FL3zEj83dk4kAS+fVpTxXLO1b38RvJgT/0QwvV/L3aY9TAnyv0EOqW4GoMQ==",
+          "dev": true
+        },
+        "yocto-queue": {
+          "version": "1.1.1",
+          "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-1.1.1.tgz",
+          "integrity": "sha512-b4JR1PFR10y1mKjhHY9LaGo6tmrgjit7hxVIeAmyMw3jegXR4dhYqLaQF5zMXZxY7tLpMyJeLjr1C4rLmkVe8g==",
+          "dev": true
+        }
+      }
+    },
+    "@commitlint/types": {
+      "version": "19.5.0",
+      "resolved": "https://registry.npmjs.org/@commitlint/types/-/types-19.5.0.tgz",
+      "integrity": "sha512-DSHae2obMSMkAtTBSOulg5X7/z+rGLxcXQIkg3OmWvY6wifojge5uVMydfhUvs7yQj+V7jNmRZ2Xzl8GJyqRgg==",
+      "dev": true,
+      "requires": {
+        "@types/conventional-commits-parser": "^5.0.0",
+        "chalk": "^5.3.0"
+      },
+      "dependencies": {
+        "chalk": {
+          "version": "5.4.1",
+          "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.4.1.tgz",
+          "integrity": "sha512-zgVZuo2WcZgfUEmsn6eO3kINexW8RAE4maiQ8QNs8CtpPCSyMiYsULR3HQYkm3w8FIA3SberyMJMSldGsW+U3w==",
+          "dev": true
+        }
+      }
+    },
     "@eslint-community/eslint-utils": {
       "version": "4.4.0",
       "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz",
@@ -17687,12 +18881,30 @@
       "dev": true,
       "peer": true
     },
+    "@types/conventional-commits-parser": {
+      "version": "5.0.1",
+      "resolved": "https://registry.npmjs.org/@types/conventional-commits-parser/-/conventional-commits-parser-5.0.1.tgz",
+      "integrity": "sha512-7uz5EHdzz2TqoMfV7ee61Egf5y6NkcO4FB/1iCCQnbeiI1F3xzv3vK5dBCXUCLQgGYS+mUeigK1iKQzvED+QnQ==",
+      "dev": true,
+      "requires": {
+        "@types/node": "*"
+      }
+    },
     "@types/minimist": {
       "version": "1.2.5",
       "resolved": "https://registry.npmjs.org/@types/minimist/-/minimist-1.2.5.tgz",
       "integrity": "sha512-hov8bUuiLiyFPGyFPE1lwWhmzYbirOXQNNo40+y3zow8aFVTeyn3VWL0VFFfdNddA8S4Vf0Tc062rzyNr7Paag==",
       "dev": true
     },
+    "@types/node": {
+      "version": "22.13.1",
+      "resolved": "https://registry.npmjs.org/@types/node/-/node-22.13.1.tgz",
+      "integrity": "sha512-jK8uzQlrvXqEU91UxiK5J7pKHyzgnI1Qnl0QDHIgVGuolJhRb9EEl28Cj9b3rGR8B2lhFCtvIm5os8lFnO/1Ew==",
+      "dev": true,
+      "requires": {
+        "undici-types": "~6.20.0"
+      }
+    },
     "@types/normalize-package-data": {
       "version": "2.4.4",
       "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz",
@@ -18241,6 +19453,15 @@
         "q": "^1.5.1"
       }
     },
+    "conventional-changelog-conventionalcommits": {
+      "version": "7.0.2",
+      "resolved": "https://registry.npmjs.org/conventional-changelog-conventionalcommits/-/conventional-changelog-conventionalcommits-7.0.2.tgz",
+      "integrity": "sha512-NKXYmMR/Hr1DevQegFB4MwfM5Vv0m4UIxKZTTYuD98lpTknaZlSRrDOG4X7wIXpGkfsYxZTghUN+Qq+T0YQI7w==",
+      "dev": true,
+      "requires": {
+        "compare-func": "^2.0.0"
+      }
+    },
     "conventional-changelog-writer": {
       "version": "5.0.1",
       "resolved": "https://registry.npmjs.org/conventional-changelog-writer/-/conventional-changelog-writer-5.0.1.tgz",
@@ -18328,6 +19549,12 @@
       "integrity": "sha512-v1plID3y9r/lPhviJ1wrXpLeyUIGAZ2SHNYTEapm7/8A9nLPoyvVp3RK/EPFqn5kEznyWgYZNsRtYYIWbuG8KA==",
       "dev": true
     },
+    "dargs": {
+      "version": "8.1.0",
+      "resolved": "https://registry.npmjs.org/dargs/-/dargs-8.1.0.tgz",
+      "integrity": "sha512-wAV9QHOsNbwnWdNW2FYvE1P56wtgSbM+3SZcdGiWQILwVjACCXDCI3Ai8QlCjMDB8YK5zySiXZYBiwGmNY3lnw==",
+      "dev": true
+    },
     "dateformat": {
       "version": "3.0.3",
       "resolved": "https://registry.npmjs.org/dateformat/-/dateformat-3.0.3.tgz",
@@ -18607,6 +19834,12 @@
         }
       }
     },
+    "env-paths": {
+      "version": "2.2.1",
+      "resolved": "https://registry.npmjs.org/env-paths/-/env-paths-2.2.1.tgz",
+      "integrity": "sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==",
+      "dev": true
+    },
     "error-ex": {
       "version": "1.3.2",
       "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz",
@@ -19079,6 +20312,12 @@
       "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==",
       "dev": true
     },
+    "fast-uri": {
+      "version": "3.0.6",
+      "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.0.6.tgz",
+      "integrity": "sha512-Atfo14OibSv5wAp4VWNsFYE1AchQRTv9cBGWET4pZWHzYshFSS9NQI6I57rdKn9croWVMbYFbLhJ+yJvmZIIHw==",
+      "dev": true
+    },
     "fastq": {
       "version": "1.15.0",
       "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.15.0.tgz",
@@ -19401,6 +20640,31 @@
         }
       }
     },
+    "git-raw-commits": {
+      "version": "4.0.0",
+      "resolved": "https://registry.npmjs.org/git-raw-commits/-/git-raw-commits-4.0.0.tgz",
+      "integrity": "sha512-ICsMM1Wk8xSGMowkOmPrzo2Fgmfo4bMHLNX6ytHjajRJUqvHOw/TFapQ+QG75c3X/tTDDhOSRPGC52dDbNM8FQ==",
+      "dev": true,
+      "requires": {
+        "dargs": "^8.0.0",
+        "meow": "^12.0.1",
+        "split2": "^4.0.0"
+      },
+      "dependencies": {
+        "meow": {
+          "version": "12.1.1",
+          "resolved": "https://registry.npmjs.org/meow/-/meow-12.1.1.tgz",
+          "integrity": "sha512-BhXM0Au22RwUneMPwSCnyhTOizdWoIEPU9sp0Aqa1PnDMR5Wv2FGXYDjuzJEIX+Eo2Rb8xuYe5jrnm5QowQFkw==",
+          "dev": true
+        },
+        "split2": {
+          "version": "4.2.0",
+          "resolved": "https://registry.npmjs.org/split2/-/split2-4.2.0.tgz",
+          "integrity": "sha512-UcjcJOWknrNkF6PLX83qcHM6KHgVKNkV62Y8a5uYDVv9ydGQVwAHMKqHdJje1VTWpljG0WYpCDhrCdAOYH4TWg==",
+          "dev": true
+        }
+      }
+    },
     "glob": {
       "version": "7.2.3",
       "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz",
@@ -19424,6 +20688,23 @@
         "is-glob": "^4.0.3"
       }
     },
+    "global-directory": {
+      "version": "4.0.1",
+      "resolved": "https://registry.npmjs.org/global-directory/-/global-directory-4.0.1.tgz",
+      "integrity": "sha512-wHTUcDUoZ1H5/0iVqEudYW4/kAlN5cZ3j/bXn0Dpbizl9iaUVeWSHqiOjsgk6OW2bkLclbBjzewBz6weQ1zA2Q==",
+      "dev": true,
+      "requires": {
+        "ini": "4.1.1"
+      },
+      "dependencies": {
+        "ini": {
+          "version": "4.1.1",
+          "resolved": "https://registry.npmjs.org/ini/-/ini-4.1.1.tgz",
+          "integrity": "sha512-QQnnxNyfvmHFIsj7gkPcYymR8Jdw/o7mp5ZFihxn6h8Ci6fh3Dx4E1gPjpQEpIuPo9XVNY/ZUwh4BPMjGyL01g==",
+          "dev": true
+        }
+      }
+    },
     "globals": {
       "version": "11.12.0",
       "resolved": "https://registry.npmjs.org/globals/-/globals-11.12.0.tgz",
@@ -19668,8 +20949,7 @@
       "version": "4.0.0",
       "resolved": "https://registry.npmjs.org/import-meta-resolve/-/import-meta-resolve-4.0.0.tgz",
       "integrity": "sha512-okYUR7ZQPH+efeuMJGlq4f8ubUgO50kByRPyt/Cy1Io4PSRsPjxME+YlVaCOx+NIToW7hCsZNFJyTPFFKepRSA==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "imurmurhash": {
       "version": "0.1.4",
@@ -20217,6 +21497,12 @@
       "dev": true,
       "peer": true
     },
+    "jiti": {
+      "version": "2.4.2",
+      "resolved": "https://registry.npmjs.org/jiti/-/jiti-2.4.2.tgz",
+      "integrity": "sha512-rg9zJN+G4n2nfJl5MW3BMygZX56zKPNVEYYqq7adpmMh4Jn2QNEwhvQlFy6jPVdcod7txZtKHWnyZiA3a0zP7A==",
+      "dev": true
+    },
     "js-md5": {
       "version": "0.7.3",
       "resolved": "https://registry.npmjs.org/js-md5/-/js-md5-0.7.3.tgz",
@@ -20459,6 +21745,12 @@
       "dev": true,
       "peer": true
     },
+    "lodash.camelcase": {
+      "version": "4.3.0",
+      "resolved": "https://registry.npmjs.org/lodash.camelcase/-/lodash.camelcase-4.3.0.tgz",
+      "integrity": "sha512-TwuEnCnxbc3rAvhf/LbG7tJUDzhqXyFnv3dtzLOPgCG/hODL7WFnsbwktkD7yUV0RrreP/l1PALq/YSg6VvjlA==",
+      "dev": true
+    },
     "lodash.capitalize": {
       "version": "4.2.1",
       "resolved": "https://registry.npmjs.org/lodash.capitalize/-/lodash.capitalize-4.2.1.tgz",
@@ -20495,18 +21787,54 @@
       "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==",
       "dev": true
     },
+    "lodash.kebabcase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.kebabcase/-/lodash.kebabcase-4.1.1.tgz",
+      "integrity": "sha512-N8XRTIMMqqDgSy4VLKPnJ/+hpGZN+PHQiJnSenYqPaVV/NCqEogTnAdZLQiGKhxX+JCs8waWq2t1XHWKOmlY8g==",
+      "dev": true
+    },
     "lodash.merge": {
       "version": "4.6.2",
       "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz",
       "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==",
       "dev": true
     },
+    "lodash.mergewith": {
+      "version": "4.6.2",
+      "resolved": "https://registry.npmjs.org/lodash.mergewith/-/lodash.mergewith-4.6.2.tgz",
+      "integrity": "sha512-GK3g5RPZWTRSeLSpgP8Xhra+pnjBC56q9FZYe1d5RN3TJ35dbkGy3YqBSMbyCrlbi+CM9Z3Jk5yTL7RCsqboyQ==",
+      "dev": true
+    },
+    "lodash.snakecase": {
+      "version": "4.1.1",
+      "resolved": "https://registry.npmjs.org/lodash.snakecase/-/lodash.snakecase-4.1.1.tgz",
+      "integrity": "sha512-QZ1d4xoBHYUeuouhEq3lk3Uq7ldgyFXGBhg04+oRLnIz8o9T65Eh+8YdroUwn846zchkA9yDsDl5CVVaV2nqYw==",
+      "dev": true
+    },
+    "lodash.startcase": {
+      "version": "4.4.0",
+      "resolved": "https://registry.npmjs.org/lodash.startcase/-/lodash.startcase-4.4.0.tgz",
+      "integrity": "sha512-+WKqsK294HMSc2jEbNgpHpd0JfIBhp7rEV4aqXWqFr6AlXov+SlcgB1Fv01y2kGe3Gc8nMW7VA0SrGuSkRfIEg==",
+      "dev": true
+    },
+    "lodash.uniq": {
+      "version": "4.5.0",
+      "resolved": "https://registry.npmjs.org/lodash.uniq/-/lodash.uniq-4.5.0.tgz",
+      "integrity": "sha512-xfBaXQd9ryd9dlSDvnvI0lvxfLJlYAZzXomUYzLKtUeOQvOP5piqAWuGtrhWeqaXK9hhoM/iyJc5AV+XfsX3HQ==",
+      "dev": true
+    },
     "lodash.uniqby": {
       "version": "4.7.0",
       "resolved": "https://registry.npmjs.org/lodash.uniqby/-/lodash.uniqby-4.7.0.tgz",
       "integrity": "sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==",
       "dev": true
     },
+    "lodash.upperfirst": {
+      "version": "4.3.1",
+      "resolved": "https://registry.npmjs.org/lodash.upperfirst/-/lodash.upperfirst-4.3.1.tgz",
+      "integrity": "sha512-sReKOYJIJf74dhJONhU4e0/shzi1trVbSWDOhKYE5XV2O+H7Sb2Dihwuc7xWxVl+DgFPyTqIN3zMfT9cq5iWDg==",
+      "dev": true
+    },
     "loose-envify": {
       "version": "1.4.0",
       "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
@@ -23395,6 +24723,12 @@
       "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==",
       "dev": true
     },
+    "require-from-string": {
+      "version": "2.0.2",
+      "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz",
+      "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==",
+      "dev": true
+    },
     "require-main-filename": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/require-main-filename/-/require-main-filename-2.0.0.tgz",
@@ -28390,6 +29724,12 @@
         }
       }
     },
+    "tinyexec": {
+      "version": "0.3.2",
+      "resolved": "https://registry.npmjs.org/tinyexec/-/tinyexec-0.3.2.tgz",
+      "integrity": "sha512-KQQR9yN7R5+OSwaK0XQoj22pwHoTlgYqmUscPYoknOoWCWfj/5/ABTMRi69FrKU5ffPVh5QcFikpWJI/P1ocHA==",
+      "dev": true
+    },
     "to-fast-properties": {
       "version": "2.0.0",
       "resolved": "https://registry.npmjs.org/to-fast-properties/-/to-fast-properties-2.0.0.tgz",
@@ -28505,6 +29845,13 @@
         "is-typedarray": "^1.0.0"
       }
     },
+    "typescript": {
+      "version": "5.7.3",
+      "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.3.tgz",
+      "integrity": "sha512-84MVSjMEHP+FQRPy3pX9sTVV/INIex71s9TL2Gm5FG/WG1SqXeKyZ0k7/blY/4FdOzI12CBy1vGc4og/eus0fw==",
+      "dev": true,
+      "peer": true
+    },
     "uglify-js": {
       "version": "3.17.4",
       "resolved": "https://registry.npmjs.org/uglify-js/-/uglify-js-3.17.4.tgz",
@@ -28525,6 +29872,12 @@
         "which-boxed-primitive": "^1.0.2"
       }
     },
+    "undici-types": {
+      "version": "6.20.0",
+      "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.20.0.tgz",
+      "integrity": "sha512-Ny6QZ2Nju20vw1SRHe3d9jVu6gJ+4e3+MMpqu7pqE5HT6WsTSlce++GQmK5UXS8mzV8DSYHrQH+Xrf2jVcuKNg==",
+      "dev": true
+    },
     "unicode-emoji-modifier-base": {
       "version": "1.0.0",
       "resolved": "https://registry.npmjs.org/unicode-emoji-modifier-base/-/unicode-emoji-modifier-base-1.0.0.tgz",
@@ -28545,8 +29898,7 @@
       "version": "0.1.0",
       "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz",
       "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==",
-      "dev": true,
-      "peer": true
+      "dev": true
     },
     "unique-string": {
       "version": "2.0.0",
diff --git a/package.json b/package.json
index 9450e16..e797563 100644
--- a/package.json
+++ b/package.json
@@ -20,6 +20,8 @@
   },
   "devDependencies": {
     "@babel/eslint-parser": "^7.5.4",
+    "@commitlint/cli": "^19.7.1",
+    "@commitlint/config-conventional": "^19.7.1",
     "eslint": "^8.16.0",
     "eslint-config-scratch": "^7.0.0",
     "scratch-semantic-release-config": "1.0.8",