diff --git a/.eslintrc.json b/.eslintrc.json index b4d7b30..111280a 100644 --- a/.eslintrc.json +++ b/.eslintrc.json @@ -1,28 +1,21 @@ { - "env": { - "browser": true, - "es6": true, - "jest": true - }, - "extends": [ - "eslint:recommended", - "plugin:@typescript-eslint/recommended" - ], - "ignorePatterns": [ - "**/*.d.ts", - "**/*.js", - "dist" - ], - "parser": "@typescript-eslint/parser", - "plugins": ["@typescript-eslint"], - "parserOptions": { - "ecmaVersion": 2020, - "sourceType": "module" - }, - "root": true, - "rules": { - "@typescript-eslint/indent": ["error", 2], - "no-multiple-empty-lines": ["error", {"max": 1, "maxEOF": 0}], - "no-trailing-spaces": "error" - } -} \ No newline at end of file + "env": { + "browser": true, + "es6": true, + "jest": true + }, + "extends": ["eslint:recommended", "plugin:@typescript-eslint/recommended"], + "ignorePatterns": ["**/*.d.ts", "**/*.js", "dist", "cjs", "esm", "iife"], + "parser": "@typescript-eslint/parser", + "plugins": ["@typescript-eslint"], + "parserOptions": { + "ecmaVersion": 2020, + "sourceType": "module" + }, + "root": true, + "rules": { + "@typescript-eslint/indent": ["error", 2], + "no-multiple-empty-lines": ["error", { "max": 1, "maxEOF": 0 }], + "no-trailing-spaces": "error" + } +} diff --git a/.prettierrc b/.prettierrc new file mode 100644 index 0000000..544138b --- /dev/null +++ b/.prettierrc @@ -0,0 +1,3 @@ +{ + "singleQuote": true +} diff --git a/package-lock.json b/package-lock.json index 91e2f7d..5c767b6 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14,9 +14,10 @@ "@typescript-eslint/eslint-plugin": "^4.20.0", "@typescript-eslint/parser": "^4.20.0", "cspell": "^5.3.9", - "eslint": "^7.23.0", + "eslint": "^7.32.0", "husky": "^6.0.0", "jest": "^26.6.3", + "prettier": "3.8.1", "rollup": "^2.44.0", "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.5.4", @@ -889,15 +890,16 @@ "dev": true }, "node_modules/@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -908,30 +910,40 @@ "node": "^10.12.0 || >=12.0.0" } }, - "node_modules/@eslint/eslintrc/node_modules/globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "dependencies": { - "type-fest": "^0.8.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/@eslint/eslintrc/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } }, + "node_modules/@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + }, + "engines": { + "node": ">=10.10.0" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -1451,10 +1463,11 @@ } }, "node_modules/@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", - "dev": true + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, "node_modules/@types/node": { "version": "14.6.0", @@ -1687,10 +1700,11 @@ } }, "node_modules/acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } @@ -1717,10 +1731,11 @@ } }, "node_modules/ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1733,10 +1748,11 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1880,6 +1896,7 @@ "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -2200,19 +2217,6 @@ "node": ">=0.10.0" } }, - "node_modules/call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -2520,10 +2524,11 @@ } }, "node_modules/cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, + "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", @@ -2856,13 +2861,21 @@ } }, "node_modules/debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", - "deprecated": "Debug versions >=3.2.0 <3.2.7 || >=4 <4.3.1 have a low-severity ReDos regression when used in a Node.js environment. It is recommended you upgrade to 3.2.7 or 4.3.1. (https://github.com/visionmedia/debug/issues/797)", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, + "license": "MIT", "dependencies": { - "ms": "^2.1.1" + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, "node_modules/decamelize": { @@ -2999,6 +3012,7 @@ "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, + "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, @@ -3073,12 +3087,14 @@ } }, "node_modules/enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, + "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8.6" @@ -3194,28 +3210,33 @@ } }, "node_modules/eslint": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", - "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", + "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, + "license": "MIT", "dependencies": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -3224,7 +3245,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -3233,7 +3254,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -3298,15 +3319,30 @@ "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", "dev": true, + "license": "MIT", "dependencies": { "@babel/highlight": "^7.10.4" } }, + "node_modules/eslint/node_modules/escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/eslint/node_modules/ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -3316,6 +3352,7 @@ "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "acorn": "^7.4.0", "acorn-jsx": "^5.3.1", @@ -3330,6 +3367,7 @@ "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", "dev": true, + "license": "Apache-2.0", "engines": { "node": ">=4" } @@ -3348,10 +3386,11 @@ } }, "node_modules/esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, @@ -3360,10 +3399,11 @@ } }, "node_modules/esquery/node_modules/estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } @@ -3697,7 +3737,8 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-equals": { "version": "3.0.3", @@ -3734,6 +3775,23 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "node_modules/fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ], + "license": "BSD-3-Clause" + }, "node_modules/fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -3803,10 +3861,11 @@ } }, "node_modules/flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", - "dev": true + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", + "dev": true, + "license": "ISC" }, "node_modules/for-in": { "version": "1.0.2", @@ -3917,20 +3976,6 @@ "node": "6.* || 8.* || >= 10.*" } }, - "node_modules/get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "dependencies": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -4018,10 +4063,11 @@ } }, "node_modules/globals": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz", - "integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, + "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, @@ -4037,6 +4083,7 @@ "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, + "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, @@ -4107,18 +4154,6 @@ "node": ">=8" } }, - "node_modules/has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -4255,10 +4290,11 @@ } }, "node_modules/ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, + "license": "MIT", "engines": { "node": ">= 4" } @@ -4365,21 +4401,6 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "node_modules/is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", - "dev": true, - "dependencies": { - "call-bind": "^1.0.0" - }, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -4510,10 +4531,11 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, + "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, @@ -4530,18 +4552,6 @@ "node": ">=0.12.0" } }, - "node_modules/is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -4578,18 +4588,6 @@ "node": ">=0.10.0" } }, - "node_modules/is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true, - "engines": { - "node": ">= 0.4" - }, - "funding": { - "url": "https://github.com/sponsors/ljharb" - } - }, "node_modules/is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -5432,7 +5430,8 @@ "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -5528,23 +5527,19 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "node_modules/lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "node_modules/lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", - "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, + "license": "MIT" }, "node_modules/lodash.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true, + "license": "MIT" }, "node_modules/lru-cache": { "version": "5.1.1", @@ -5720,10 +5715,11 @@ } }, "node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, "node_modules/nanomatch": { "version": "1.2.13", @@ -6159,6 +6155,22 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true, + "license": "MIT", + "bin": { + "prettier": "bin/prettier.cjs" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/prettier/prettier?sponsor=1" + } + }, "node_modules/pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -6179,6 +6191,7 @@ "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.4.0" } @@ -6366,6 +6379,7 @@ "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, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -6874,6 +6888,7 @@ "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "astral-regex": "^2.0.0", @@ -7191,14 +7206,15 @@ } }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" @@ -7248,6 +7264,7 @@ "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" }, @@ -7287,35 +7304,33 @@ "dev": true }, "node_modules/table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=10.0.0" } }, "node_modules/table/node_modules/ajv": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.1.tgz", - "integrity": "sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw==", + "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, + "license": "MIT", "dependencies": { - "fast-deep-equal": "^3.1.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" }, "funding": { "type": "github", @@ -7326,7 +7341,8 @@ "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 + "dev": true, + "license": "MIT" }, "node_modules/terminal-link": { "version": "2.1.1", @@ -7397,8 +7413,9 @@ "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" }, "node_modules/throat": { "version": "5.0.0", @@ -7751,10 +7768,11 @@ } }, "node_modules/uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -7796,10 +7814,11 @@ } }, "node_modules/v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", + "dev": true, + "license": "MIT" }, "node_modules/v8-to-istanbul": { "version": "7.1.1", @@ -8825,15 +8844,15 @@ "dev": true }, "@eslint/eslintrc": { - "version": "0.4.0", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.0.tgz", - "integrity": "sha512-2ZPCc+uNbjV5ERJr+aKSPRwZgKd2z11x0EgLvb1PURmUrn9QNRXFqje0Ldq454PfAVyaJYyrDvvIKSFP4NnBog==", + "version": "0.4.3", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", + "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", "dev": true, "requires": { "ajv": "^6.12.4", "debug": "^4.1.1", "espree": "^7.3.0", - "globals": "^12.1.0", + "globals": "^13.9.0", "ignore": "^4.0.6", "import-fresh": "^3.2.1", "js-yaml": "^3.13.1", @@ -8841,15 +8860,6 @@ "strip-json-comments": "^3.1.1" }, "dependencies": { - "globals": { - "version": "12.4.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-12.4.0.tgz", - "integrity": "sha512-BWICuzzDvDoH54NHKCseDanAhE3CeDorgDL5MT6LMXXj2WCnd9UC2szdk4AWLfjdgNBCXLUanXYcpBBKOSWGwg==", - "dev": true, - "requires": { - "type-fest": "^0.8.1" - } - }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -8858,6 +8868,23 @@ } } }, + "@humanwhocodes/config-array": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", + "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "dev": true, + "requires": { + "@humanwhocodes/object-schema": "^1.2.0", + "debug": "^4.1.1", + "minimatch": "^3.0.4" + } + }, + "@humanwhocodes/object-schema": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.1.tgz", + "integrity": "sha512-ZnQMnLV4e7hDlUvw8H+U8ASL02SS2Gn6+9Ac3wGGLIe7+je2AeAOxPY+izIPJDfFDb7eDjev0Us8MO1iFRN8hA==", + "dev": true + }, "@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", @@ -9300,9 +9327,9 @@ } }, "@types/json-schema": { - "version": "7.0.7", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.7.tgz", - "integrity": "sha512-cxWFQVseBm6O9Gbw1IWb8r6OS4OhSt3hPZLkFApLjM8TEXROBuQGLAH2i2gZpcXdLBIrpXuTDhH7Vbm1iXmNGA==", + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true }, "@types/node": { @@ -9456,9 +9483,9 @@ } }, "acorn-jsx": { - "version": "5.3.1", - "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.1.tgz", - "integrity": "sha512-K0Ptm/47OKfQRpNQ2J/oIN/3QYiK6FwW+eJbILhsdxh2WTLdl+30o8aGdTbm5JbffpFFAg/g+zi1E+jvJha5ng==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", + "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "requires": {} }, @@ -9478,9 +9505,9 @@ } }, "ajv": { - "version": "6.12.5", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.5.tgz", - "integrity": "sha512-lRF8RORchjpKG50/WFf8xmg7sgCLFiYNNnqdKflk63whMQcWR5ngGjiSXkL9bjxy6B2npOK2HSMN49jEBMSkag==", + "version": "6.12.6", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", + "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -9490,9 +9517,9 @@ } }, "ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true }, "ansi-escapes": { @@ -9838,16 +9865,6 @@ "unset-value": "^1.0.0" } }, - "call-bind": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/call-bind/-/call-bind-1.0.2.tgz", - "integrity": "sha512-7O+FbCihrB5WGbFYesctwmTKae6rOiIzmz1icreWJ+0aA7LJfuqhEso2T9ncpcFtzMQtzXf2QGGueWJGTYsqrA==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "get-intrinsic": "^1.0.2" - } - }, "callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", @@ -10086,9 +10103,9 @@ } }, "cross-spawn": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", - "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", + "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "requires": { "path-key": "^3.1.0", @@ -10334,12 +10351,12 @@ } }, "debug": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.1.1.tgz", - "integrity": "sha512-pYAIzeRo8J6KPEaJ0VWOh5Pzkbw/RetuzehGM7QRRX5he4fPHx2rdKMB256ehJCkX+XRQm16eZLqLNS8RSZXZw==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", "dev": true, "requires": { - "ms": "^2.1.1" + "ms": "^2.1.3" } }, "decamelize": { @@ -10503,12 +10520,13 @@ } }, "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.4.1.tgz", + "integrity": "sha512-rRqJg/6gd538VHvR3PSrdRBb/1Vy2YfzHqzvbhGIQpDRKIa4FgV/54b5Q1xYSxOOwKvjXweS26E0Q+nAMwp2pQ==", "dev": true, "requires": { - "ansi-colors": "^4.1.1" + "ansi-colors": "^4.1.1", + "strip-ansi": "^6.0.1" } }, "error-ex": { @@ -10593,28 +10611,31 @@ } }, "eslint": { - "version": "7.23.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.23.0.tgz", - "integrity": "sha512-kqvNVbdkjzpFy0XOszNwjkKzZ+6TcwCQ/h+ozlcIWwaimBBuhlQ4nN6kbiM2L+OjDcznkTJxzYfRFH92sx4a0Q==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.0", + "@eslint/eslintrc": "^0.4.3", + "@humanwhocodes/config-array": "^0.5.0", "ajv": "^6.10.0", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.0.1", "doctrine": "^3.0.0", "enquirer": "^2.3.5", + "escape-string-regexp": "^4.0.0", "eslint-scope": "^5.1.1", "eslint-utils": "^2.1.0", "eslint-visitor-keys": "^2.0.0", "espree": "^7.3.1", "esquery": "^1.4.0", "esutils": "^2.0.2", + "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.0.0", + "glob-parent": "^5.1.2", "globals": "^13.6.0", "ignore": "^4.0.6", "import-fresh": "^3.0.0", @@ -10623,7 +10644,7 @@ "js-yaml": "^3.13.1", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", - "lodash": "^4.17.21", + "lodash.merge": "^4.6.2", "minimatch": "^3.0.4", "natural-compare": "^1.4.0", "optionator": "^0.9.1", @@ -10632,7 +10653,7 @@ "semver": "^7.2.1", "strip-ansi": "^6.0.0", "strip-json-comments": "^3.1.0", - "table": "^6.0.4", + "table": "^6.0.9", "text-table": "^0.2.0", "v8-compile-cache": "^2.0.3" }, @@ -10646,6 +10667,12 @@ "@babel/highlight": "^7.10.4" } }, + "escape-string-regexp": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", + "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", + "dev": true + }, "ignore": { "version": "4.0.6", "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", @@ -10713,18 +10740,18 @@ "dev": true }, "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", + "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "requires": { "estraverse": "^5.1.0" }, "dependencies": { "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true } } @@ -11030,6 +11057,12 @@ "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", "dev": true }, + "fast-uri": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.0.tgz", + "integrity": "sha512-iPeeDKJSWf4IEOasVVrknXpaBV0IApz/gp7S2bb7Z4Lljbl2MGJRqInZiUrQwV16cpzw/D3S5j5Julj/gT52AA==", + "dev": true + }, "fastq": { "version": "1.11.0", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.0.tgz", @@ -11087,9 +11120,9 @@ } }, "flatted": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.1.1.tgz", - "integrity": "sha512-zAoAQiudy+r5SvnSw3KJy5os/oRJYHzrzja/tBDqrZtNhUw8bt6y8OBzMWcjWr+8liV8Eb6yOhw8WZ7VFZ5ZzA==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.3.tgz", + "integrity": "sha512-GX+ysw4PBCz0PzosHDepZGANEuFCMLrnRTiEy9McGjmkCQYwRq4A/X786G/fjM/+OjsWSU1ZrY5qyARZmO/uwg==", "dev": true }, "for-in": { @@ -11172,17 +11205,6 @@ "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true }, - "get-intrinsic": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.1.1.tgz", - "integrity": "sha512-kWZrnVM42QCiEA2Ig1bG8zjoIMOgxWwYCEeNdwY6Tv/cOSeGpcoX4pXHfKUxNKVoArnrEr2e9srnAxxGIraS9Q==", - "dev": true, - "requires": { - "function-bind": "^1.1.1", - "has": "^1.0.3", - "has-symbols": "^1.0.1" - } - }, "get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", @@ -11243,9 +11265,9 @@ } }, "globals": { - "version": "13.7.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.7.0.tgz", - "integrity": "sha512-Aipsz6ZKRxa/xQkZhNg0qIWXT6x6rD46f6x/PCnBomlttdIyAPak4YD9jTmKpZ72uROSMU87qJtcgpgHaVchiA==", + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -11307,12 +11329,6 @@ "integrity": "sha512-Pq0h+hvsVm6dDEa8x82GnLSYHOzNDt7f0ddFa3FqcQlgzEiptPqL+XrOJNavjOzSYiYWIrgeVYYgGlLmnxwilQ==", "dev": true }, - "has-symbols": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.0.2.tgz", - "integrity": "sha512-chXa79rL/UC2KlX17jo3vRGz0azaWEx5tGqZg5pO3NUyEJVB17dMruQlzCCOfUvElghKcm5194+BCRvi2Rv/Gw==", - "dev": true - }, "has-value": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/has-value/-/has-value-1.0.0.tgz", @@ -11420,9 +11436,9 @@ "dev": true }, "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true }, "import-fresh": { @@ -11507,15 +11523,6 @@ "integrity": "sha1-d8mYQFJ6qOyxqLppe4BkWnqSap0=", "dev": true }, - "is-boolean-object": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/is-boolean-object/-/is-boolean-object-1.1.0.tgz", - "integrity": "sha512-a7Uprx8UtD+HWdyYwnD1+ExtTgqQtD2k/1yJgtXP6wnMm8byhkoTZRl+95LLThpzNZJ5aEvi46cdH+ayMFRwmA==", - "dev": true, - "requires": { - "call-bind": "^1.0.0" - } - }, "is-buffer": { "version": "1.1.6", "resolved": "https://registry.npmjs.org/is-buffer/-/is-buffer-1.1.6.tgz", @@ -11611,9 +11618,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, "requires": { "is-extglob": "^2.1.1" @@ -11625,12 +11632,6 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, - "is-number-object": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/is-number-object/-/is-number-object-1.0.4.tgz", - "integrity": "sha512-zohwelOAur+5uXtk8O3GPQ1eAcu4ZX3UwxQhUlfFFMNpUd83gXgjbhJh6HmB6LUNV/ieOLQuDwJO3dWJosUeMw==", - "dev": true - }, "is-obj": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/is-obj/-/is-obj-2.0.0.tgz", @@ -11658,12 +11659,6 @@ "integrity": "sha1-EtSj3U5o4Lec6428hBc66A2RykQ=", "dev": true }, - "is-string": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/is-string/-/is-string-1.0.5.tgz", - "integrity": "sha512-buY6VNRjhQMiF1qWDouloZlQbRhDPCebwxSjxMjxgemYT46YMd2NR0/H+fBhEfWX4A/w9TBJ+ol+okqJKFE6vQ==", - "dev": true - }, "is-typedarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/is-typedarray/-/is-typedarray-1.0.0.tgz", @@ -12399,22 +12394,16 @@ "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==", "dev": true }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.flatten": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/lodash.flatten/-/lodash.flatten-4.4.0.tgz", - "integrity": "sha1-8xwiIlqWMtK7+OSt2+8kCqdlph8=", + "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.truncate": { "version": "4.4.2", "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", "dev": true }, "lru-cache": { @@ -12553,9 +12542,9 @@ } }, "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", "dev": true }, "nanomatch": { @@ -12893,6 +12882,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "3.8.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz", + "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==", + "dev": true + }, "pretty-format": { "version": "26.6.2", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-26.6.2.tgz", @@ -13709,14 +13704,14 @@ } }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { @@ -13778,32 +13773,28 @@ "dev": true }, "table": { - "version": "6.0.9", - "resolved": "https://registry.npmjs.org/table/-/table-6.0.9.tgz", - "integrity": "sha512-F3cLs9a3hL1Z7N4+EkSscsel3z55XT950AvB05bwayrNg5T1/gykXtigioTAjbltvbMSJvvhFCbnf6mX+ntnJQ==", + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", "dev": true, "requires": { "ajv": "^8.0.1", - "is-boolean-object": "^1.1.0", - "is-number-object": "^1.0.4", - "is-string": "^1.0.5", - "lodash.clonedeep": "^4.5.0", - "lodash.flatten": "^4.4.0", "lodash.truncate": "^4.4.2", "slice-ansi": "^4.0.0", - "string-width": "^4.2.0" + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" }, "dependencies": { "ajv": { - "version": "8.0.1", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.0.1.tgz", - "integrity": "sha512-46ZA4TalFcLLqX1dEU3dhdY38wAtDydJ4e7QQTVekLUTzXkb1LfqU6VOBXC/a9wiv4T094WURqJH6ZitF92Kqw==", + "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.1", + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" + "require-from-string": "^2.0.2" } }, "json-schema-traverse": { @@ -13864,7 +13855,7 @@ "text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", "dev": true }, "throat": { @@ -14124,9 +14115,9 @@ } }, "uri-js": { - "version": "4.4.0", - "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.0.tgz", - "integrity": "sha512-B0yRTzYdUCCn9n+F4+Gh4yIDtMQcaJsmYBDsTSG8g/OejKBodLQ2IHfN3bM7jUsRXndopT7OIXWdYqc1fjmV6g==", + "version": "4.4.1", + "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", + "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "requires": { "punycode": "^2.1.0" @@ -14162,9 +14153,9 @@ "optional": true }, "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", + "version": "2.4.0", + "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.4.0.tgz", + "integrity": "sha512-ocyWc3bAHBB/guyqJQVI5o4BZkPhznPYUG2ea80Gond/BgNWpap8TOmLSeeQG7bnh2KMISxskdADG59j7zruhw==", "dev": true }, "v8-to-istanbul": { diff --git a/package.json b/package.json index 46e527a..5405857 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "module": "./esm/ISNCSCI.js", "scripts": { "build": "rollup -c", - "test": "jest && eslint **/*.ts && cspell **", + "test": "jest && eslint \"src/**/*.ts\" \"specs/**/*.ts\" \"demo/**/*.ts\" && cspell **", "test:watch": "jest --watch" }, "author": "Praxis Spinal Cord Institute", @@ -30,9 +30,10 @@ "@typescript-eslint/eslint-plugin": "^4.20.0", "@typescript-eslint/parser": "^4.20.0", "cspell": "^5.3.9", - "eslint": "^7.23.0", + "eslint": "^7.32.0", "husky": "^6.0.0", "jest": "^26.6.3", + "prettier": "3.8.1", "rollup": "^2.44.0", "rollup-plugin-terser": "^7.0.2", "ts-jest": "^26.5.4", diff --git a/src/classification/asiaImpairmentScale/asiaImpairmentScale.spec.ts b/src/classification/asiaImpairmentScale/asiaImpairmentScale.spec.ts index 9e742f9..18ec828 100644 --- a/src/classification/asiaImpairmentScale/asiaImpairmentScale.spec.ts +++ b/src/classification/asiaImpairmentScale/asiaImpairmentScale.spec.ts @@ -1,8 +1,4 @@ -import { Exam } from "../../interfaces"; -import { newEmptyExam } from "../commonSpec"; - describe('asiaImpairmentScale', () => { - const exam: Exam = newEmptyExam(); xit('temp', () => { expect(undefined).toBeDefined(); }) diff --git a/src/classification/commonSpec.ts b/src/classification/commonSpec.ts index 6a0c0e0..19cfaf4 100644 --- a/src/classification/commonSpec.ts +++ b/src/classification/commonSpec.ts @@ -49,7 +49,7 @@ export const newEmptyExam = (): Exam => { }; } -export function propagateSensoryValueFrom(side:ExamSide, level: SensoryLevel, value: SensoryPointValue) { +export function propagateSensoryValueFrom(side: ExamSide, level: SensoryLevel, value: SensoryPointValue): void { for (let i = SensoryLevels.indexOf(level); i { state.currentLevel = currentLevel; }); - it('`checkForMotorFunction` throws an exception when the `currentLevel` in the state object is null', () => { + it('`checkForMotorFunction` throws MotorZPPError when the `currentLevel` in the state object is null', () => { state.currentLevel = null; - expect(() => checkLevel(state)).toThrowError('checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.'); + expect(() => checkLevel(state)).toThrow(MotorZPPError); + expect(() => checkLevel(state)).toThrowError( + 'checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.', + ); }); it('calls `checkForMotorFunction`', () => { @@ -395,9 +400,12 @@ describe('motorZPP', () => { state.currentLevel = currentLevel; }); - it('`checkForMotorFunction` throws an exception when the `currentLevel` in the state object is null', () => { + it('`checkForMotorFunction` throws MotorZPPError when the `currentLevel` in the state object is null', () => { state.currentLevel = null; - expect(() => checkLevel(state)).toThrowError('checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.'); + expect(() => checkLevel(state)).toThrow(MotorZPPError); + expect(() => checkLevel(state)).toThrowError( + 'checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.', + ); }); it('calls `checkForMotorFunction`', () => { @@ -413,4 +421,64 @@ describe('motorZPP', () => { }); }); }); + + /* *************************************** */ + /* motorZPPSteps tests */ + /* *************************************** */ + + describe('motorZPPSteps', () => { + beforeEach(() => { + side = newNormalSide(); + }); + + it('yields at least one step', () => { + const steps = Array.from(motorZPPSteps(side, 'Yes', 'E', 'S3')); + expect(steps.length).toBeGreaterThanOrEqual(1); + }); + + it('final step result matches determineMotorZPP for same inputs', () => { + side.lightTouch.S4_5 = '0'; + side.pinPrick.S4_5 = '0'; + propagateSensoryValueFrom(side, 'T5', '0'); + propagateMotorValueFrom(side, 'L2', '0'); + + const expected = determineMotorZPP(side, 'No', 'C*', 'T4'); + const steps = Array.from(motorZPPSteps(side, 'No', 'C*', 'T4')); + const lastStep = steps[steps.length - 1]; + const actual = lastStep.state.zpp.join(','); + + expect(actual).toBe(expected); + }); + + it('VAC = Yes yields 1 step and stops (next is null)', () => { + const steps = Array.from(motorZPPSteps(side, 'Yes', 'E', 'INT')); + expect(steps).toHaveLength(1); + expect(steps[0].next).toBeNull(); + expect(steps[0].state.zpp).toEqual(['NA']); + }); + + it('VAC = No yields multiple steps for full calculation', () => { + side.lightTouch.S4_5 = '0'; + side.pinPrick.S4_5 = '0'; + propagateSensoryValueFrom(side, 'T5', '0'); + propagateMotorValueFrom(side, 'L2', '0'); + + const steps = Array.from(motorZPPSteps(side, 'No', 'C*', 'T4')); + expect(steps.length).toBeGreaterThan(1); + expect(steps[steps.length - 1].next).toBeNull(); + }); + + it('each step has description, actions, state, and next', () => { + const steps = Array.from(motorZPPSteps(side, 'Yes', 'E', 'INT')); + for (const step of steps) { + expect(step).toHaveProperty('description'); + expect(step).toHaveProperty('actions'); + expect(step).toHaveProperty('state'); + expect(step).toHaveProperty('next'); + expect(step.description).toHaveProperty('key'); + expect(Array.isArray(step.actions)).toBe(true); + expect(step.state).toHaveProperty('zpp'); + } + }); + }); }); diff --git a/src/classification/zoneOfPartialPreservation/motorZPP.ts b/src/classification/zoneOfPartialPreservation/motorZPP.ts index 26e6ba4..574c174 100644 --- a/src/classification/zoneOfPartialPreservation/motorZPP.ts +++ b/src/classification/zoneOfPartialPreservation/motorZPP.ts @@ -1,143 +1,292 @@ -import {BinaryObservation, ExamSide, MotorLevel, MotorLevels, SensoryLevel, SensoryLevels} from '../../interfaces'; -import {SideLevel, Translation} from '../common'; +import { + BinaryObservation, + ExamSide, + MotorLevel, + MotorLevels, + SensoryLevel, + SensoryLevels, +} from '../../interfaces'; +import { SideLevel, Translation } from '../common'; +import { + MotorZPPError, + MOTOR_ZPP_ERROR_MESSAGES, +} from './motorZPPErrors'; + +export { MotorZPPError, MOTOR_ZPP_ERROR_MESSAGES }; + +/* *************************************** */ +/* Constants - Regex patterns */ +/* *************************************** */ + +const PATTERNS = { + /** Contains asterisk (variable/non-normal indicator) */ + hasVariableIndicator: /\*/, + /** Normal sensory: 2 or ends with ** */ + normalSensory: /(^2$)|(\*\*$)/, + /** Normal motor: 5 or ends with ** */ + normalMotor: /(^5$)|(\*\*$)/, + /** Motor grade 0* (impaired with variable) */ + motorZeroWithStar: /0\*/, + /** Digit followed by * (e.g. 1*, 2* in sensory) */ + digitWithStar: /\d\*/, + /** + * Motor values indicating motor function (excludes 0 and 0* which mean no function). + * Matches: grades 1-5 (and variants like 1*, 2**, etc.), NT**, or 0**. + */ + hasMotorValueExcludingZeroStar: /^[1-5]|^(?:NT|0)\*\*$/, + /** Not tested or 0*: NT, NT*, or 0* */ + notTestedOrZeroStar: /^(NT\*?$)|(0\*$)/, + /** Levels below S1: S2, S3, or INT (independent of motor or sensory) */ + levelBelowS1: /(S2|S3|INT)\*?(,|$)/, + /** AIS grade C or C* */ + aisGradeC: /C/i, + /** Asterisk(s) to strip from level names */ + stripAsterisk: /\*/g, + /** Single asterisk for replacement */ + singleAsterisk: /\*/, +} as const; /* *************************************** */ /* Types */ /* *************************************** */ export type State = { - ais: string, - motorLevel: string, - voluntaryAnalContraction: BinaryObservation, - zpp: string[], - topLevel: SideLevel, - bottomLevel: SideLevel, - currentLevel: SideLevel | null, - side: ExamSide, - nonKeyMuscle: SideLevel | null, - nonKeyMuscleHasBeenAdded: boolean, - testNonKeyMuscle: boolean, - addNonKeyMuscle: boolean, - firstLevelWithStar: SideLevel | null, - lastLevelWithConsecutiveNormalValues: SideLevel, -} + ais: string; + motorLevel: string; + voluntaryAnalContraction: BinaryObservation; + zpp: string[]; + topLevel: SideLevel; + bottomLevel: SideLevel; + currentLevel: SideLevel | null; + side: ExamSide; + nonKeyMuscle: SideLevel | null; + nonKeyMuscleHasBeenAdded: boolean; + testNonKeyMuscle: boolean; + addNonKeyMuscle: boolean; + firstLevelWithStar: SideLevel | null; + lastLevelWithConsecutiveNormalValues: SideLevel; +}; + +export type StepHandler = (state: State) => Step; export type Step = { - description: {key: Translation, params?: {[key: string]: string}}; - actions: {key: Translation, params?: {[key: string]: string}}[]; - next: ((state: State) => Step) | null; + description: { key: Translation; params?: { [key: string]: string } }; + actions: { key: Translation; params?: { [key: string]: string } }[]; + next: StepHandler | null; state: State; -} +}; /* *************************************** */ /* Support methods */ /* *************************************** */ -/* - * Iterates down the side and builds a chain of `SideLevel` objects with only the levels that need to be checked. - * It also maps the top, bottom, nonKeyMuscle, firstLevelWithStar, and lastLevelWithConsecutiveNormalValues. - * Can throw the following exception: - * 'Unable to determine the topLevel, bottomLevel, or lastLevelWithConsecutiveNormalValues' - * This happens when the side has invalid or missing values or the provided top or bottom level are calculated incorrectly. - */ -function getLevelsRange(side: ExamSide, top: SensoryLevel, bottom: SensoryLevel, nonKeyMuscleName: MotorLevel | null): { - topLevel: SideLevel, - bottomLevel: SideLevel, - nonKeyMuscle: SideLevel | null, - firstLevelWithStar: SideLevel | null, - lastLevelWithConsecutiveNormalValues: SideLevel, -} { - let currentLevel: SideLevel | null = null; - let topLevel: SideLevel | null = null; - let bottomLevel: SideLevel | null = null; - let nonKeyMuscle: SideLevel | null = null; - let firstLevelWithStar: SideLevel | null = null; - let lastLevelWithConsecutiveNormalValues: SideLevel | null = null; - - for (let i = 0; i < SensoryLevels.length && !bottomLevel; i++) { +function createSideLevel( + side: ExamSide, + sensoryLevelName: SensoryLevel, + index: number, +): SideLevel { + const motorLevelName: MotorLevel | null = MotorLevels.includes( + sensoryLevelName as MotorLevel, + ) + ? (sensoryLevelName as MotorLevel) + : null; + return { + name: sensoryLevelName, + lightTouch: + sensoryLevelName === 'C1' ? '2' : side.lightTouch[sensoryLevelName], + pinPrick: + sensoryLevelName === 'C1' ? '2' : side.pinPrick[sensoryLevelName], + motor: motorLevelName ? side.motor[motorLevelName] : null, + index, + next: null, + previous: null, + }; +} + +/** Creates SideLevel objects for each SensoryLevel from C1 through bottom. */ +function buildLevelsFromC1ToBottom( + side: ExamSide, + bottom: SensoryLevel, +): SideLevel[] { + const levels: SideLevel[] = []; + const bottomIndex = SensoryLevels.indexOf(bottom); + for (let i = 0; i <= bottomIndex; i++) { const sensoryLevelName = SensoryLevels[i]; - const motorLevelName: MotorLevel | null = MotorLevels.includes(sensoryLevelName as MotorLevel) ? sensoryLevelName as MotorLevel : null; - - const level: SideLevel = { - name: sensoryLevelName, - lightTouch: sensoryLevelName === 'C1' ? '2' : side.lightTouch[sensoryLevelName], - pinPrick: sensoryLevelName === 'C1' ? '2' : side.pinPrick[sensoryLevelName], - motor: motorLevelName ? side.motor[motorLevelName] : null, - index: i, - next: null, - previous: null, - }; + levels.push(createSideLevel(side, sensoryLevelName, i)); + } + return levels; +} - if (!firstLevelWithStar - && ( - /\*/.test(level.lightTouch) - || /\*/.test(level.pinPrick) - || /\*/.test(level.motor ?? '') - ) - ) { - firstLevelWithStar = level; +/** Links levels from top to bottom with next/previous. Returns topLevel and bottomLevel. */ +function linkLevelChain( + levels: SideLevel[], + top: SensoryLevel, + bottom: SensoryLevel, +): { topLevel: SideLevel; bottomLevel: SideLevel } { + const topIndex = SensoryLevels.indexOf(top); + const bottomIndex = SensoryLevels.indexOf(bottom); + const topLevel = levels[topIndex]; + const bottomLevel = levels[bottomIndex]; + + for (let i = topIndex; i <= bottomIndex; i++) { + const level = levels[i]; + if (i > topIndex) { + const prev = levels[i - 1]; + prev.next = level; + level.previous = prev; } + } + return { topLevel, bottomLevel }; +} - if (!lastLevelWithConsecutiveNormalValues - && ( - !/(^2$)|(\*\*$)/.test(level.lightTouch) - || !/(^2$)|(\*\*$)/.test(level.pinPrick) - || !/(^5$)|(\*\*$)/.test(level.motor ?? '') - ) +/** First level (in iteration order) that has * in lightTouch, pinPrick, or motor. */ +function findFirstLevelWithStar(levels: SideLevel[]): SideLevel | null { + for (const level of levels) { + if ( + PATTERNS.hasVariableIndicator.test(level.lightTouch) || + PATTERNS.hasVariableIndicator.test(level.pinPrick) || + PATTERNS.hasVariableIndicator.test(level.motor ?? '') ) { - lastLevelWithConsecutiveNormalValues = currentLevel; - } - - if (motorLevelName && motorLevelName === nonKeyMuscleName) { - nonKeyMuscle = level; + return level; } + } + return null; +} - if (top === sensoryLevelName) { - currentLevel = level; - topLevel = level; - } else if (currentLevel) { - currentLevel.next = level; - level.previous = currentLevel; - currentLevel = level; +/** + * Last level before the first non-normal value within the top-to-bottom range. + * If all levels in range are normal, returns bottomLevel. + */ +function findLastLevelWithConsecutiveNormalValues( + levels: SideLevel[], + top: SensoryLevel, + bottom: SensoryLevel, + bottomLevel: SideLevel, +): SideLevel { + const topIndex = SensoryLevels.indexOf(top); + const bottomIndex = SensoryLevels.indexOf(bottom); + for (let i = topIndex; i <= bottomIndex; i++) { + const level = levels[i]; + const sensoryNormal = + PATTERNS.normalSensory.test(level.lightTouch) && + PATTERNS.normalSensory.test(level.pinPrick); + const motorNormal = + level.motor === null || PATTERNS.normalMotor.test(level.motor); + const isNormal = sensoryNormal && motorNormal; + if (!isNormal) { + return i > topIndex ? levels[i - 1] : bottomLevel; } + } + return bottomLevel; +} - if (bottom === sensoryLevelName) { - bottomLevel = currentLevel; +function findNonKeyMuscle( + levels: SideLevel[], + nonKeyMuscleName: MotorLevel | null, +): SideLevel | null { + if (!nonKeyMuscleName) return null; + return levels.find((l) => l.name === nonKeyMuscleName) ?? null; +} - if (!lastLevelWithConsecutiveNormalValues) { - lastLevelWithConsecutiveNormalValues = currentLevel; - } - } - } +/* + * Composes support functions to build the level chain and find top, bottom, nonKeyMuscle, + * firstLevelWithStar, and lastLevelWithConsecutiveNormalValues. + * Can throw: 'Unable to determine the topLevel, bottomLevel, or lastLevelWithConsecutiveNormalValues' + */ +function getLevelsRange( + side: ExamSide, + top: SensoryLevel, + bottom: SensoryLevel, + nonKeyMuscleName: MotorLevel | null, +): { + topLevel: SideLevel; + bottomLevel: SideLevel; + nonKeyMuscle: SideLevel | null; + firstLevelWithStar: SideLevel | null; + lastLevelWithConsecutiveNormalValues: SideLevel; + } { + const levels = buildLevelsFromC1ToBottom(side, bottom); + const { topLevel, bottomLevel } = linkLevelChain(levels, top, bottom); + const firstLevelWithStar = findFirstLevelWithStar(levels); + const lastLevelWithConsecutiveNormalValues = + findLastLevelWithConsecutiveNormalValues(levels, top, bottom, bottomLevel); + const nonKeyMuscle = findNonKeyMuscle(levels, nonKeyMuscleName); if (!topLevel || !bottomLevel || !lastLevelWithConsecutiveNormalValues) { - throw new Error('getLevelsRange :: Unable to determine the topLevel, bottomLevel, or lastLevelWithConsecutiveNormalValues'); + throw new MotorZPPError('LEVELS_RANGE_UNDETERMINED'); } - return {topLevel, bottomLevel, nonKeyMuscle, firstLevelWithStar, lastLevelWithConsecutiveNormalValues}; + return { + topLevel, + bottomLevel, + nonKeyMuscle, + firstLevelWithStar, + lastLevelWithConsecutiveNormalValues, + }; } /* * Returns `true` if there is a level flagged with `*` above the `currentLevel` or if `currentLevel` has it. */ -function hasStarOnCurrentOrAboveLevel(currentLevel: SideLevel, lastLevelWithConsecutiveNormalValues: SideLevel, firstLevelWithStar: SideLevel | null): boolean { - // Good example, case #93 +function hasStarOnCurrentOrAboveLevel( + currentLevel: SideLevel, + lastLevelWithConsecutiveNormalValues: SideLevel, + firstLevelWithStar: SideLevel | null, +): boolean { + // An example of a case where firstLevelWithStar is null is case #93 in `specs/2019.json`. if (!firstLevelWithStar) { return false; } if (currentLevel.motor !== null) { - return /0\*/.test(currentLevel.motor); + return PATTERNS.motorZeroWithStar.test(currentLevel.motor); } - if (/\d\*/.test(currentLevel.lightTouch) || /\d\*/.test(currentLevel.pinPrick)) { + const hasStarOnCurrentLevel = + PATTERNS.digitWithStar.test(currentLevel.lightTouch) || + PATTERNS.digitWithStar.test(currentLevel.pinPrick); + if (hasStarOnCurrentLevel) { return true; } - // return currentLevel.index <= lastLevelWithConsecutiveNormalValues.index && currentLevel.index >= firstLevelWithStar.index; - return /\d\*/.test(currentLevel.lightTouch) - || /\d\*/.test(currentLevel.pinPrick) - || (currentLevel.index <= lastLevelWithConsecutiveNormalValues.index && currentLevel.index >= firstLevelWithStar.index); + const isInStarRange = + currentLevel.index >= firstLevelWithStar.index && + currentLevel.index <= lastLevelWithConsecutiveNormalValues.index; + return isInStarRange; +} + +/** Non-key muscle overrides when it is >3 levels below the current level and we are testing for it. */ +function shouldOverrideWithNonKeyMuscle( + state: State, + currentLevel: SideLevel, +): boolean { + if (!state.testNonKeyMuscle || !state.nonKeyMuscle) { + return false; + } + return state.nonKeyMuscle.index - currentLevel.index > 3; +} + +function isAtTopOfRange(currentLevel: SideLevel, state: State): boolean { + return currentLevel.name === state.topLevel.name; +} + +function buildMotorZPPLevelName(levelName: string, hasStar: boolean): string { + return `${levelName}${hasStar ? '*' : ''}`; +} + +function createStep( + description: Step['description'], + actions: Step['actions'], + state: State, + updates: Partial, + next: Step['next'], +): Step { + return { + description, + actions, + state: { ...state, ...updates }, + next, + }; } /* *************************************** */ @@ -149,21 +298,31 @@ function hasStarOnCurrentOrAboveLevel(currentLevel: SideLevel, lastLevelWithCons * Sorts the ZPP results ensuring the `NA` value, if available, is at the beginning of the list. */ export function sortMotorZPP(state: State): Step { - const zpp = state.zpp.sort((a, b) => { - const aIndex = a === 'NA' ? -1 : SensoryLevels.indexOf(a.replace(/\*/, '') as SensoryLevel); - const bIndex = b === 'NA' ? -1 : SensoryLevels.indexOf(b.replace(/\*/, '') as SensoryLevel); + const zpp = [...state.zpp].sort((a, b) => { + const aIndex = + a === 'NA' + ? -1 + : SensoryLevels.indexOf( + a.replace(PATTERNS.singleAsterisk, '') as SensoryLevel, + ); + const bIndex = + b === 'NA' + ? -1 + : SensoryLevels.indexOf( + b.replace(PATTERNS.singleAsterisk, '') as SensoryLevel, + ); return aIndex - bIndex; }); return { - description: {key: 'motorZPPSortMotorZPPDescription'}, - actions: [{key: 'motorZPPSortMotorZPPEnsureNAIsPlacedFirstAction'}], + description: { key: 'motorZPPSortMotorZPPDescription' }, + actions: [{ key: 'motorZPPSortMotorZPPEnsureNAIsPlacedFirstAction' }], state: { ...state, zpp, }, next: null, - } + }; } /* @@ -172,12 +331,20 @@ export function sortMotorZPP(state: State): Step { * It sets `sortMotorZPP` as the next and final step. */ export function addLowerNonKeyMuscleToMotorZPPIfNeeded(state: State): Step { - const description: {key: Translation} = {key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededDescription'}; + const description: { key: Translation } = { + key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededDescription', + }; - return state.addNonKeyMuscle && !state.nonKeyMuscleHasBeenAdded && state.nonKeyMuscle + return state.addNonKeyMuscle && + !state.nonKeyMuscleHasBeenAdded && + state.nonKeyMuscle ? { description, - actions: [{key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededAddNonKeyMuscleAction'}], + actions: [ + { + key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededAddNonKeyMuscleAction', + }, + ], state: { ...state, zpp: [...state.zpp, state.nonKeyMuscle.name], @@ -186,7 +353,11 @@ export function addLowerNonKeyMuscleToMotorZPPIfNeeded(state: State): Step { } : { description, - actions: [{key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededIgnoreNonKeyMuscleAction'}], + actions: [ + { + key: 'motorZPPAddLowerNonKeyMuscleToMotorZPPIfNeededIgnoreNonKeyMuscleAction', + }, + ], state: { ...state, zpp: [...state.zpp], @@ -208,11 +379,13 @@ export function addLowerNonKeyMuscleToMotorZPPIfNeeded(state: State): Step { */ export function checkForSensoryFunction(state: State): Step { if (!state.currentLevel) { - throw new Error('checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.'); + throw new MotorZPPError( + 'CHECK_FOR_SENSORY_FUNCTION_CURRENT_LEVEL_REQUIRED', + ); } const currentLevel = state.currentLevel; - const description: {key: Translation, params: {[key: string]: string}} = { + const description: { key: Translation; params: { [key: string]: string } } = { key: 'motorZPPCheckForSensoryFunctionDescription', params: { levelName: currentLevel.name, @@ -220,47 +393,70 @@ export function checkForSensoryFunction(state: State): Step { pinPrick: currentLevel.pinPrick, }, }; - const isTopRange = currentLevel.name === state.topLevel.name; + const atTop = isAtTopOfRange(currentLevel, state); + const nextStep = atTop ? addLowerNonKeyMuscleToMotorZPPIfNeeded : checkLevel; if (state.motorLevel.includes(currentLevel.name)) { - const hasStar = hasStarOnCurrentOrAboveLevel(currentLevel, state.lastLevelWithConsecutiveNormalValues, state.firstLevelWithStar); - const motorZPPName = `${currentLevel.name}${hasStar ? '*' : ''}`; - const overrideWithNonKeyMuscle = state.testNonKeyMuscle && state.nonKeyMuscle !== null && state.nonKeyMuscle.index - currentLevel.index > 3; - const actions: {key: Translation, params?: {[index: string]: string}}[] = [ - {key: 'motorZPPCheckForSensoryFunctionLevelIncludedInMotorValuesAction', params: {levelName: currentLevel.name}}, - {key: overrideWithNonKeyMuscle ? 'motorZPPCheckForSensoryFunctionLevelIncludedButOverriddenByNonKeyMuscleAction' : 'motorZPPCheckForSensoryFunctionAddLevelAndContinueAction'}, + const hasStar = hasStarOnCurrentOrAboveLevel( + currentLevel, + state.lastLevelWithConsecutiveNormalValues, + state.firstLevelWithStar, + ); + const override = shouldOverrideWithNonKeyMuscle(state, currentLevel); + const actions: { + key: Translation; + params?: { [index: string]: string }; + }[] = [ + { + key: 'motorZPPCheckForSensoryFunctionLevelIncludedInMotorValuesAction', + params: { levelName: currentLevel.name }, + }, + { + key: override + ? 'motorZPPCheckForSensoryFunctionLevelIncludedButOverriddenByNonKeyMuscleAction' + : 'motorZPPCheckForSensoryFunctionAddLevelAndContinueAction', + }, ]; - - if (isTopRange) { - actions.push({key: 'motorZPPCheckForSensoryFunctionTopOfRangeReachedStopAction'}); + if (atTop) { + actions.push({ + key: 'motorZPPCheckForSensoryFunctionTopOfRangeReachedStopAction', + }); } - return { + return createStep( description, actions, - state: { - ...state, - zpp: overrideWithNonKeyMuscle ? [...state.zpp] : [motorZPPName, ...state.zpp], + state, + { + zpp: override ? [...state.zpp] : [buildMotorZPPLevelName(currentLevel.name, hasStar), ...state.zpp], currentLevel: currentLevel.previous, - addNonKeyMuscle: state.addNonKeyMuscle || overrideWithNonKeyMuscle, + addNonKeyMuscle: state.addNonKeyMuscle || override, }, - next: isTopRange ? addLowerNonKeyMuscleToMotorZPPIfNeeded : checkLevel, - }; + nextStep, + ); } - return isTopRange - ? { - description, - actions: [{key: 'motorZPPCheckForSensoryFunctionTopOfRangeReachedStopAction'}], - state: {...state, zpp: [...state.zpp]}, - next: addLowerNonKeyMuscleToMotorZPPIfNeeded, - } - : { + if (atTop) { + return createStep( description, - actions: [{key: 'motorZPPCheckForSensoryFunctionNoSensoryFunctionFoundContinueAction'}], - state: {...state, zpp: [...state.zpp], currentLevel: currentLevel.previous}, - next: checkLevel, - }; + [{ key: 'motorZPPCheckForSensoryFunctionTopOfRangeReachedStopAction' }], + state, + { zpp: [...state.zpp] }, + nextStep, + ); + } + + return createStep( + description, + [ + { + key: 'motorZPPCheckForSensoryFunctionNoSensoryFunctionFoundContinueAction', + }, + ], + state, + { zpp: [...state.zpp], currentLevel: currentLevel.previous }, + nextStep, + ); } /* @@ -281,106 +477,139 @@ export function checkForSensoryFunction(state: State): Step { */ export function checkForMotorFunction(state: State): Step { if (!state.currentLevel) { - throw new Error('checkForMotorFunction :: state.currentLevel is null. A SideLevel value is required.'); + throw new MotorZPPError( + 'CHECK_FOR_MOTOR_FUNCTION_CURRENT_LEVEL_REQUIRED', + ); } const currentLevel = state.currentLevel; - if (!currentLevel.motor) { - throw new Error('checkForMotorFunction :: state.currentLevel.motor is null.'); + throw new MotorZPPError('CHECK_FOR_MOTOR_FUNCTION_MOTOR_REQUIRED'); } - const isNonKeyMuscle = state.nonKeyMuscle ? currentLevel.name === state.nonKeyMuscle.name : false; - const overrideWithNonKeyMuscle = state.testNonKeyMuscle && state.nonKeyMuscle && state.nonKeyMuscle.index - currentLevel.index > 3; - const description: {key: Translation, params: {[key: string]: string}} = {key: 'motorZPPCheckForMotorFunctionDescription', params: {levelName: currentLevel.name, motor: currentLevel.motor}}; - const isTopRangeLevel = currentLevel.name === state.topLevel.name; + const description: { key: Translation; params: { [key: string]: string } } = { + key: 'motorZPPCheckForMotorFunctionDescription', + params: { levelName: currentLevel.name, motor: currentLevel.motor }, + }; + const atTop = isAtTopOfRange(currentLevel, state); + const override = shouldOverrideWithNonKeyMuscle(state, currentLevel); + const isNonKeyMuscle = state.nonKeyMuscle?.name === currentLevel.name; + const nextWhenAtTop = addLowerNonKeyMuscleToMotorZPPIfNeeded; + const nextWhenNotAtTop = checkLevel; + const nextStep = atTop ? nextWhenAtTop : nextWhenNotAtTop; + + const hasStar = hasStarOnCurrentOrAboveLevel( + currentLevel, + state.lastLevelWithConsecutiveNormalValues, + state.firstLevelWithStar, + ); - // This will skip 0*, which needs to be handled individually - if (/^[1-5]/.test(currentLevel.motor) || /^(NT|[0-4])\*\*$/.test(currentLevel.motor)) { - const hasStar = hasStarOnCurrentOrAboveLevel(currentLevel, state.lastLevelWithConsecutiveNormalValues, state.firstLevelWithStar); + const baseStateUpdates = { + currentLevel: currentLevel.previous, + }; + const overrideStateUpdates = { + ...baseStateUpdates, + addNonKeyMuscle: true, + }; + const addLevelStateUpdates = (zppAddition: string) => ({ + ...baseStateUpdates, + zpp: [zppAddition, ...state.zpp], + nonKeyMuscleHasBeenAdded: + state.nonKeyMuscleHasBeenAdded || isNonKeyMuscle, + }); - return overrideWithNonKeyMuscle - ? { + // Case 1: Motor function (grades 1-5, NT**, 0**) – add level and stop + if (PATTERNS.hasMotorValueExcludingZeroStar.test(currentLevel.motor)) { + if (override) { + return createStep( description, - actions: [{key: 'motorZPPCheckForMotorFunctionNonKeyMuscleOverrideAndStopAction'}], - state: { - ...state, - zpp: [...state.zpp], - currentLevel: currentLevel.previous, - addNonKeyMuscle: true, - }, - next: addLowerNonKeyMuscleToMotorZPPIfNeeded, - } - : { - description, - actions: [{key: 'motorZPPCheckForMotorFunctionAddLevelAndStopAction'}], - state: { - ...state, - zpp: [`${currentLevel.name}${hasStar ? '*' : ''}`, ...state.zpp], - currentLevel: currentLevel.previous, - nonKeyMuscleHasBeenAdded: state.nonKeyMuscleHasBeenAdded || isNonKeyMuscle, - }, - next: addLowerNonKeyMuscleToMotorZPPIfNeeded, - }; + [ + { + key: 'motorZPPCheckForMotorFunctionNonKeyMuscleOverrideAndStopAction', + }, + ], + state, + { ...overrideStateUpdates, zpp: [...state.zpp] }, + nextWhenAtTop, + ); + } + return createStep( + description, + [{ key: 'motorZPPCheckForMotorFunctionAddLevelAndStopAction' }], + state, + addLevelStateUpdates(buildMotorZPPLevelName(currentLevel.name, hasStar)), + nextWhenAtTop, + ); } - if (/^(NT\*?$)|(0\*$)/.test(currentLevel.motor)) { - const actions: {key: Translation}[] = [ - {key: isTopRangeLevel ? 'motorZPPCheckForMotorFunctionStopAtTopAction' : 'motorZPPCheckForMotorFunctionContinueUntilTopAction'}, + // Case 2: NT or 0* – add level and continue (or override) + if (PATTERNS.notTestedOrZeroStar.test(currentLevel.motor)) { + const rangeActions: { key: Translation }[] = [ + { + key: atTop + ? 'motorZPPCheckForMotorFunctionStopAtTopAction' + : 'motorZPPCheckForMotorFunctionContinueUntilTopAction', + }, ]; - - const hasStar = hasStarOnCurrentOrAboveLevel(currentLevel, state.lastLevelWithConsecutiveNormalValues, state.firstLevelWithStar); - if (hasStar) { - actions.push({key: 'motorZPPCheckForMotorFunctionAddStarAction'}); + rangeActions.push({ key: 'motorZPPCheckForMotorFunctionAddStarAction' }); } - return overrideWithNonKeyMuscle - ? { - description, - actions: [ - {key: 'motorZPPCheckForMotorFunctionFunctionFoundButKeyMuscleOverrideAction'}, - ...actions, - ], - state: { - ...state, - zpp: [...state.zpp], - currentLevel: currentLevel.previous, - addNonKeyMuscle: true, - }, - next: isTopRangeLevel ? addLowerNonKeyMuscleToMotorZPPIfNeeded : checkLevel, - } - : { + if (override) { + return createStep( description, - actions: [ - {key: 'motorZPPCheckForMotorFunctionAddLevelWithNormalFunctionAndContinue'}, - ...actions, + [ + { + key: 'motorZPPCheckForMotorFunctionFunctionFoundButKeyMuscleOverrideAction', + }, + ...rangeActions, ], - state: { - ...state, - zpp: [`${currentLevel.name}${hasStar ? '*' : ''}`, ...state.zpp], - currentLevel: currentLevel.previous, - nonKeyMuscleHasBeenAdded: state.nonKeyMuscleHasBeenAdded || isNonKeyMuscle, + state, + { ...overrideStateUpdates, zpp: [...state.zpp] }, + nextStep, + ); + } + return createStep( + description, + [ + { + key: 'motorZPPCheckForMotorFunctionAddLevelWithNormalFunctionAndContinue', }, - next: isTopRangeLevel ? addLowerNonKeyMuscleToMotorZPPIfNeeded : checkLevel, - }; + ...rangeActions, + ], + state, + addLevelStateUpdates(buildMotorZPPLevelName(currentLevel.name, hasStar)), + nextStep, + ); } - if (currentLevel.name === state.topLevel.name) { - return { + // Case 3: At top of range with no function – stop + if (atTop) { + return createStep( description, - actions: [{key: 'motorZPPCheckForMotorFunctionTopOfRangeReachedStopAction'}], - state: {...state, currentLevel: currentLevel.previous}, - next: addLowerNonKeyMuscleToMotorZPPIfNeeded, - }; + [ + { + key: 'motorZPPCheckForMotorFunctionTopOfRangeReachedStopAction', + }, + ], + state, + baseStateUpdates, + nextWhenAtTop, + ); } - return { + // Case 4: No function found – continue + return createStep( description, - actions: [{key: 'motorZPPCheckForMotorFunctionNoFunctionFoundContinueAction'}], - state: {...state, currentLevel: currentLevel.previous}, - next: checkLevel, - }; + [ + { + key: 'motorZPPCheckForMotorFunctionNoFunctionFoundContinueAction', + }, + ], + state, + baseStateUpdates, + nextWhenNotAtTop, + ); } /* @@ -401,32 +630,47 @@ export function checkLevel(state: State): Step { * It sets `currentLevel = bottom` and a reference to `nonKeyMuscle` if one was specified. */ export function getTopAndBottomLevelsForCheck(state: State): Step { - const motorLevels = state.motorLevel.replace(/\*/g, '').split(','); + const motorLevels = state.motorLevel + .replace(PATTERNS.stripAsterisk, '') + .split(','); const top = motorLevels[0] as SensoryLevel; const lowestMotorLevel = motorLevels[motorLevels.length - 1]; // We exclude not normal S1 values as there would be no propagation for that case - const hasMotorBelowS1 = /(S2|S3|INT)\*?(,|$)/.test(state.motorLevel); + const hasMotorBelowS1 = PATTERNS.levelBelowS1.test(state.motorLevel); const bottom = hasMotorBelowS1 - ? lowestMotorLevel === 'INT' ? 'S3' : lowestMotorLevel as SensoryLevel + ? lowestMotorLevel === 'INT' + ? 'S3' + : (lowestMotorLevel as SensoryLevel) : 'S1'; - const includeS10OrLowerAction: {key: Translation} = { + const includeS10OrLowerAction: { key: Translation } = { key: hasMotorBelowS1 ? 'motorZPPGetTopAndBottomLevelsForCheckIncludeBelowS1Action' - : 'motorZPPGetTopAndBottomLevelsForCheckDoNotIncludeBelowS1Action' + : 'motorZPPGetTopAndBottomLevelsForCheckDoNotIncludeBelowS1Action', }; - const {topLevel, bottomLevel, nonKeyMuscle, firstLevelWithStar, lastLevelWithConsecutiveNormalValues} = getLevelsRange( + const { + topLevel, + bottomLevel, + nonKeyMuscle, + firstLevelWithStar, + lastLevelWithConsecutiveNormalValues, + } = getLevelsRange( state.side, top, bottom, - state.side.lowestNonKeyMuscleWithMotorFunction ? state.side.lowestNonKeyMuscleWithMotorFunction as MotorLevel : null, + state.side.lowestNonKeyMuscleWithMotorFunction + ? (state.side.lowestNonKeyMuscleWithMotorFunction as MotorLevel) + : null, ); return { - description: {key: 'motorZPPGetTopAndBottomLevelsForCheckDescription'}, + description: { key: 'motorZPPGetTopAndBottomLevelsForCheckDescription' }, actions: [ - {key: 'motorZPPGetTopAndBottomLevelsForCheckRangeAction', params: {bottom: bottomLevel.name, top: topLevel.name}}, + { + key: 'motorZPPGetTopAndBottomLevelsForCheckRangeAction', + params: { bottom: bottomLevel.name, top: topLevel.name }, + }, includeS10OrLowerAction, ], state: { @@ -452,15 +696,17 @@ export function getTopAndBottomLevelsForCheck(state: State): Step { */ export function checkLowerNonKeyMuscle(state: State): Step { // AIS C or C* implies that there is sensory function at S4-5 and that the lowest non-key muscle could have influenced the AIS calculation. - const testNonKeyMuscle = state.side.lowestNonKeyMuscleWithMotorFunction !== null && /C/i.test(state.ais); + const testNonKeyMuscle = + state.side.lowestNonKeyMuscleWithMotorFunction !== null && + PATTERNS.aisGradeC.test(state.ais); return { - description: {key: 'motorZPPCheckLowerNonKeyMuscleDescription'}, + description: { key: 'motorZPPCheckLowerNonKeyMuscleDescription' }, actions: [ { key: testNonKeyMuscle ? 'motorZPPCheckLowerNonKeyMuscleConsiderAction' - : 'motorZPPCheckLowerNonKeyMuscleDoNotConsiderAction' + : 'motorZPPCheckLowerNonKeyMuscleDoNotConsiderAction', }, ], state: { @@ -480,14 +726,16 @@ export function checkLowerNonKeyMuscle(state: State): Step { * If the VAC is 'No', we leave the Motor ZPP empty and continue to check for the presence of a non-key muscle. */ export function checkIfMotorZPPIsApplicable(state: State): Step { - const description: {key: Translation} = {key: 'motorZPPCheckIfMotorZPPIsApplicableDescription'}; + const description: { key: Translation } = { + key: 'motorZPPCheckIfMotorZPPIsApplicableDescription', + }; const next = checkLowerNonKeyMuscle; if (state.voluntaryAnalContraction === 'Yes') { return { description, - actions: [{key: 'motorZPPCheckIfMotorZPPIsApplicableYesAction'}], - state: {...state, zpp: ['NA']}, + actions: [{ key: 'motorZPPCheckIfMotorZPPIsApplicableYesAction' }], + state: { ...state, zpp: ['NA'] }, next: null, }; } @@ -495,16 +743,16 @@ export function checkIfMotorZPPIsApplicable(state: State): Step { if (state.voluntaryAnalContraction === 'NT') { return { description, - actions: [{key: 'motorZPPCheckIfMotorZPPIsApplicableNTAction'}], - state: {...state, zpp: ['NA']}, + actions: [{ key: 'motorZPPCheckIfMotorZPPIsApplicableNTAction' }], + state: { ...state, zpp: ['NA'] }, next, }; } return { description, - actions: [{key: 'motorZPPCheckIfMotorZPPIsApplicableNoAction'}], - state: {...state, zpp: [...state.zpp]}, + actions: [{ key: 'motorZPPCheckIfMotorZPPIsApplicableNoAction' }], + state: { ...state, zpp: [...state.zpp] }, next, }; } @@ -514,7 +762,12 @@ export function checkIfMotorZPPIsApplicable(state: State): Step { * It sets the top, bottom, and lastLevelWithConsecutiveNormalValues levels to C1, * the current level to null, and the zpp to empty array. */ -export function getInitialState(side: ExamSide, voluntaryAnalContraction: BinaryObservation, ais: string, motorLevel: string): State { +export function getInitialState( + side: ExamSide, + voluntaryAnalContraction: BinaryObservation, + ais: string, + motorLevel: string, +): State { const c1: SideLevel = { name: 'C1', lightTouch: '2', @@ -527,7 +780,7 @@ export function getInitialState(side: ExamSide, voluntaryAnalContraction: Binary return { ais, - motorLevel: motorLevel.replace(/\*/g, ''), + motorLevel: motorLevel.replace(PATTERNS.stripAsterisk, ''), voluntaryAnalContraction, zpp: [], topLevel: c1, @@ -543,21 +796,47 @@ export function getInitialState(side: ExamSide, voluntaryAnalContraction: Binary }; } -export function determineMotorZPP(side: ExamSide, voluntaryAnalContraction: BinaryObservation, ais: string, motorLevel: string): string { - let step: Step = { - description: {key: 'motorZPPCheckIfMotorZPPIsApplicableDescription'}, - actions: [], - state: getInitialState(side, voluntaryAnalContraction, ais, motorLevel), - next: checkIfMotorZPPIsApplicable, - }; +export function determineMotorZPP( + side: ExamSide, + voluntaryAnalContraction: BinaryObservation, + ais: string, + motorLevel: string, +): string { + const initialState = getInitialState( + side, + voluntaryAnalContraction, + ais, + motorLevel, + ); + let step = checkIfMotorZPPIsApplicable(initialState); while (step.next) { step = step.next(step.state); - - // ToDo: Add logger - // console.log(step.description); - // console.log(step.action); } return step.state.zpp.join(','); } + +/** + * Generator that yields each step of the motor ZPP calculation. + * Enables step-by-step execution for clinicians to see where each value is generated. + */ +export function* motorZPPSteps( + side: ExamSide, + voluntaryAnalContraction: BinaryObservation, + ais: string, + motorLevel: string, +): Generator { + const initialState = getInitialState( + side, + voluntaryAnalContraction, + ais, + motorLevel, + ); + let step = checkIfMotorZPPIsApplicable(initialState); + yield step; + while (step.next) { + step = step.next(step.state); + yield step; + } +} diff --git a/src/classification/zoneOfPartialPreservation/motorZPPErrors.ts b/src/classification/zoneOfPartialPreservation/motorZPPErrors.ts new file mode 100644 index 0000000..0b834dc --- /dev/null +++ b/src/classification/zoneOfPartialPreservation/motorZPPErrors.ts @@ -0,0 +1,36 @@ +/** + * Centralized error messages for motor ZPP calculation. + * Enables consistency and future i18n (messages can be used as translation keys). + */ +export const MOTOR_ZPP_ERROR_MESSAGES = { + LEVELS_RANGE_UNDETERMINED: + 'getLevelsRange :: Unable to determine the topLevel, bottomLevel, or lastLevelWithConsecutiveNormalValues', + CHECK_FOR_SENSORY_FUNCTION_CURRENT_LEVEL_REQUIRED: + 'checkForSensoryFunction :: state.currentLevel is null. A SideLevel value is required.', + CHECK_FOR_MOTOR_FUNCTION_CURRENT_LEVEL_REQUIRED: + 'checkForMotorFunction :: state.currentLevel is null. A SideLevel value is required.', + CHECK_FOR_MOTOR_FUNCTION_MOTOR_REQUIRED: + 'checkForMotorFunction :: state.currentLevel.motor is null.', +} as const; + +export type MotorZPPErrorCode = keyof typeof MOTOR_ZPP_ERROR_MESSAGES; + +/** + * Domain error for motor ZPP calculation failures. + * Enables programmatic error handling (e.g. by error code) and consistent messaging. + */ +export class MotorZPPError extends Error { + readonly code: MotorZPPErrorCode; + + constructor( + code: MotorZPPErrorCode, + message?: string, + ) { + const resolvedMessage = + message ?? MOTOR_ZPP_ERROR_MESSAGES[code]; + super(resolvedMessage); + this.name = 'MotorZPPError'; + this.code = code; + Object.setPrototypeOf(this, MotorZPPError.prototype); + } +}