diff --git a/.gitignore b/.gitignore index 99d757e..d42f703 100644 --- a/.gitignore +++ b/.gitignore @@ -6,4 +6,5 @@ server target dist bin -.vscode-test \ No newline at end of file +.vscode-test +src/__test__/resources/test-workspace/input/tests/results \ No newline at end of file diff --git a/.vscode-test.js b/.vscode-test.js index d99f25e..d112120 100644 --- a/.vscode-test.js +++ b/.vscode-test.js @@ -3,5 +3,14 @@ const { defineConfig } = require('@vscode/test-cli'); module.exports = defineConfig({ label: 'Tests (Empty Folder)', files: 'dist/__test__/**/*.test.js', - launchArgs: ['tests/empty', '--new-window', '--disable-extensions'], + workspaceFolder: './src/__test__/resources/test-workspace', + launchArgs: [ + 'tests/empty', + '--new-window', + '--disable-extensions', + '--disable-updates', + '--skip-welcome', + '--skip-release-notes', + '--disable-workspace-trust', + ], }); diff --git a/.vscode/extensions.json b/.vscode/extensions.json index b789f43..ad19ce7 100644 --- a/.vscode/extensions.json +++ b/.vscode/extensions.json @@ -1,18 +1,19 @@ { - // See https://go.microsoft.com/fwlink/?LinkId=827846 - // for the documentation about the extensions.json format - "recommendations": [ - "dbaeumer.vscode-eslint", - "streetsidesoftware.code-spell-checker", - "streetsidesoftware.code-spell-checker-medical-terms", - "streetsidesoftware.code-spell-checker-scientific-terms", - "sonarsource.sonarlint-vscode", - "esbenp.prettier-vscode", - "connor4312.esbuild-problem-matchers" - ], - "unwantedRecommendations": [ - "ms-vscode.vscode-typescript-tslint-plugin", - "eg2.tslint", - "eg2.vscode-npm-script", - ] -} \ No newline at end of file + // See https://go.microsoft.com/fwlink/?LinkId=827846 + // for the documentation about the extensions.json format + "recommendations": [ + "ms-vscode.extension-test-runner", + "dbaeumer.vscode-eslint", + "streetsidesoftware.code-spell-checker", + "streetsidesoftware.code-spell-checker-medical-terms", + "streetsidesoftware.code-spell-checker-scientific-terms", + "sonarsource.sonarlint-vscode", + "esbenp.prettier-vscode", + "connor4312.esbuild-problem-matchers" + ], + "unwantedRecommendations": [ + "ms-vscode.vscode-typescript-tslint-plugin", + "eg2.tslint", + "eg2.vscode-npm-script" + ] +} diff --git a/.vscode/launch.json b/.vscode/launch.json index 1b99d88..16bc7ad 100644 --- a/.vscode/launch.json +++ b/.vscode/launch.json @@ -5,12 +5,29 @@ { "version": "0.2.0", "configurations": [ + { + "name": "Extension Tests", + "type": "extensionHost", + "request": "launch", + "testConfiguration": "${workspaceFolder}/.vscode-test.js", + "preLaunchTask": "npm: compile" + }, { "name": "Run Extension", "type": "extensionHost", "request": "launch", "runtimeExecutable": "${execPath}", - "args": ["--extensionDevelopmentPath=${workspaceFolder}", "--disable-extensions"], + "args": [ + "${workspaceFolder}/src/__test__/resources/test-workspace", + "--extensionDevelopmentPath=${workspaceFolder}", + "--disable-extensions", + "--new-window", + "--disable-extensions", + "--disable-updates", + "--skip-welcome", + "--skip-release-notes", + "--disable-workspace-trust" + ], "outFiles": ["${workspaceFolder}/dist/**/*.js"], "preLaunchTask": "npm: watch" } diff --git a/package-lock.json b/package-lock.json index 75aabdf..0aac02b 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10,17 +10,18 @@ "license": "Apache-2.0", "dependencies": { "expand-tilde": "^2.0.2", - "find-java-home": "1.2.2", "fs-extra": "^8.1.0", "glob": "^10.5.0", "lodash": "^4.17.23", + "markdown-to-text": "^0.1.1", "node-fetch": "2.6.7", "tslib": "^2.4.0", "vscode-languageclient": "^7.0.0", "vscode-uri": "^3.0.8", "winreg": "^1.2.4", "winston": "^3.8.1", - "winston-daily-rotate-file": "^4.7.1" + "winston-daily-rotate-file": "^4.7.1", + "winston-transport-vscode": "^0.1.0" }, "devDependencies": { "@types/chai": "^5.2.3", @@ -33,22 +34,32 @@ "@types/node-fetch": "^2.6.2", "@types/vscode": "^1.73.0", "@types/winreg": "^1.2.31", - "@vscode/test-cli": "^0.0.4", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.3.6", "chai": "4.5", - "mocha": "^10.2.0", + "mocha": "^11.7.5", "mock-fs": "^5.5.0", - "prettier": "^3.3.3", "typescript": "^5.3.2" }, "engines": { "vscode": ">=1.75.0" } }, + "node_modules/@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + } + }, "node_modules/@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==", + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==", + "license": "MIT", "engines": { "node": ">=0.1.90" } @@ -159,6 +170,44 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, + "node_modules/@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6.0.0" + } + }, + "node_modules/@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true, + "license": "MIT" + }, + "node_modules/@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "license": "MIT", + "dependencies": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "node_modules/@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -221,6 +270,13 @@ "@types/node": "*" } }, + "node_modules/@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true, + "license": "MIT" + }, "node_modules/@types/lodash": { "version": "4.14.183", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.183.tgz", @@ -260,6 +316,12 @@ "form-data": "^3.0.0" } }, + "node_modules/@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==", + "license": "MIT" + }, "node_modules/@types/vscode": { "version": "1.85.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.85.0.tgz", @@ -273,21 +335,27 @@ "dev": true }, "node_modules/@vscode/test-cli": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.4.tgz", - "integrity": "sha512-Tx0tfbxeSb2Xlo+jpd+GJrNLgKQHobhRHrYvOipZRZQYWZ82sKiK02VY09UjU1Czc/YnZnqyAnjUfaVGl3h09w==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", + "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", "dev": true, + "license": "MIT", "dependencies": { - "@types/mocha": "^10.0.2", - "chokidar": "^3.5.3", + "@types/mocha": "^10.0.10", + "c8": "^10.1.3", + "chokidar": "^3.6.0", + "enhanced-resolve": "^5.18.3", "glob": "^10.3.10", "minimatch": "^9.0.3", - "mocha": "^10.2.0", - "supports-color": "^9.4.0", + "mocha": "^11.7.4", + "supports-color": "^10.2.2", "yargs": "^17.7.2" }, "bin": { "vscode-test": "out/bin.mjs" + }, + "engines": { + "node": ">=18" } }, "node_modules/@vscode/test-cli/node_modules/brace-expansion": { @@ -314,59 +382,6 @@ "url": "https://github.com/sponsors/isaacs" } }, - "node_modules/@vscode/test-cli/node_modules/supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "dev": true, - "engines": { - "node": ">=12" - }, - "funding": { - "url": "https://github.com/chalk/supports-color?sponsor=1" - } - }, - "node_modules/@vscode/test-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/@vscode/test-cli/node_modules/yargs/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/@vscode/test-cli/node_modules/yargs/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/@vscode/test-electron": { "version": "2.3.8", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.8.tgz", @@ -394,16 +409,6 @@ "node": ">= 6.0.0" } }, - "node_modules/ansi-colors": { - "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" - } - }, "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -427,10 +432,11 @@ } }, "node_modules/anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, + "license": "ISC", "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" @@ -443,7 +449,8 @@ "version": "2.0.1", "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", - "dev": true + "dev": true, + "license": "Python-2.0" }, "node_modules/assertion-error": { "version": "1.1.0", @@ -472,12 +479,16 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "node_modules/binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/brace-expansion": { @@ -506,13 +517,63 @@ "version": "1.3.1", "resolved": "https://registry.npmjs.org/browser-stdout/-/browser-stdout-1.3.1.tgz", "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", - "dev": true + "dev": true, + "license": "ISC" + }, + "node_modules/c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + }, + "bin": { + "c8": "bin/c8.js" + }, + "engines": { + "node": ">=18" + }, + "peerDependencies": { + "monocart-coverage-reports": "^2" + }, + "peerDependenciesMeta": { + "monocart-coverage-reports": { + "optional": true + } + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } }, "node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -544,6 +605,7 @@ "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" @@ -555,6 +617,19 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, + "node_modules/chalk/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/check-error": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/check-error/-/check-error-1.0.3.tgz", @@ -569,16 +644,11 @@ } }, "node_modules/chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, - "funding": [ - { - "type": "individual", - "url": "https://paulmillr.com/funding/" - } - ], + "license": "MIT", "dependencies": { "anymatch": "~3.1.2", "braces": "~3.0.2", @@ -591,31 +661,26 @@ "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { "fsevents": "~2.3.2" } }, - "node_modules/chokidar/node_modules/glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "dependencies": { - "is-glob": "^4.0.1" - }, - "engines": { - "node": ">= 6" - } - }, "node_modules/cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "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, + "license": "ISC", "dependencies": { "string-width": "^4.2.0", - "strip-ansi": "^6.0.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" + }, + "engines": { + "node": ">=12" } }, "node_modules/color": { @@ -691,6 +756,13 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "node_modules/convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true, + "license": "MIT" + }, "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -734,6 +806,7 @@ "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -764,15 +837,30 @@ } }, "node_modules/diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true, "license": "BSD-3-Clause", "engines": { "node": ">=0.3.1" } }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -789,6 +877,69 @@ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, + "node_modules/enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dev": true, + "license": "MIT", + "dependencies": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "license": "MIT", + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -803,6 +954,7 @@ "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" }, @@ -847,28 +999,12 @@ "node": ">=8" } }, - "node_modules/find-java-home": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/find-java-home/-/find-java-home-1.2.2.tgz", - "integrity": "sha512-rN2LcQa+YDwfnPT0TQgn015eYqJkn3h3G/uVX5oOD2Ge7gKOuoJrntAJO4BiGb+K6lo6Mb+xJdoqbfzqaC75gw==", - "dependencies": { - "which": "~1.0.5", - "winreg": "~1.2.2" - } - }, - "node_modules/find-java-home/node_modules/which": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", - "integrity": "sha512-E87fdQ/eRJr9W1X4wTPejNy9zTW3FI2vpCZSJ/HAY+TkjKVC0TUm1jk6vn2Z7qay0DQy0+RBGdXxj+RmmiGZKQ==", - "bin": { - "which": "bin/which" - } - }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, + "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" @@ -885,6 +1021,7 @@ "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", "dev": true, + "license": "BSD-3-Clause", "bin": { "flat": "cli.js" } @@ -910,13 +1047,16 @@ } }, "node_modules/form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, + "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "engines": { @@ -936,18 +1076,13 @@ "node": ">=6 <7 || >=8" } }, - "node_modules/fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "node_modules/fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, + "license": "MIT", "optional": true, "os": [ "darwin" @@ -956,6 +1091,16 @@ "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -975,6 +1120,45 @@ "node": "*" } }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "license": "MIT", + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, "node_modules/glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -996,6 +1180,19 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, "node_modules/glob/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", @@ -1014,24 +1211,80 @@ "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=16 || 14 >=14.17" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.10", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", + "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://github.com/sponsors/ljharb" } }, - "node_modules/graceful-fs": { - "version": "4.2.10", - "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", - "integrity": "sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==" - }, - "node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "node_modules/hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, + "license": "MIT", + "dependencies": { + "function-bind": "^1.1.2" + }, "engines": { - "node": ">=8" + "node": ">= 0.4" } }, "node_modules/he": { @@ -1039,6 +1292,7 @@ "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", "dev": true, + "license": "MIT", "bin": { "he": "bin/he" } @@ -1054,6 +1308,13 @@ "node": ">=0.10.0" } }, + "node_modules/html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true, + "license": "MIT" + }, "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -1087,16 +1348,6 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, - "node_modules/inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "dependencies": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -1112,6 +1363,7 @@ "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, + "license": "MIT", "dependencies": { "binary-extensions": "^2.0.0" }, @@ -1124,6 +1376,7 @@ "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1141,6 +1394,7 @@ "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" }, @@ -1158,11 +1412,22 @@ "node": ">=0.12.0" } }, + "node_modules/is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } + }, "node_modules/is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", "integrity": "sha512-YWnfyRwxL/+SsrWYfOpUtz5b3YD+nyfkHvjbcanzk8zgyO4ASD67uVMRt8k5bM4lLMDnXfriRhOpemw+NfT1eA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1183,6 +1448,7 @@ "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -1201,6 +1467,58 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "node_modules/istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true, + "license": "BSD-3-Clause", + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/istanbul-lib-report/node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "license": "MIT", + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "license": "BSD-3-Clause", + "dependencies": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -1217,10 +1535,11 @@ } }, "node_modules/js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, + "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, @@ -1267,6 +1586,7 @@ "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, + "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, @@ -1288,6 +1608,7 @@ "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, + "license": "MIT", "dependencies": { "chalk": "^4.1.0", "is-unicode-supported": "^0.1.0" @@ -1300,15 +1621,20 @@ } }, "node_modules/logform": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", - "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", + "license": "MIT", "dependencies": { - "@colors/colors": "1.5.0", + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", "triple-beam": "^1.3.0" + }, + "engines": { + "node": ">= 12.0.0" } }, "node_modules/loupe": { @@ -1332,6 +1658,54 @@ "node": ">=10" } }, + "node_modules/make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "license": "MIT", + "dependencies": { + "semver": "^7.5.3" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/markdown-to-text": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-to-text/-/markdown-to-text-0.1.1.tgz", + "integrity": "sha512-co/J5l8mJ2RK9wD/nQRGwO7JxoeyfvVNtOZll016EdAX2qYkwCWMdtYvJO42b41Ho7GFEJMuly9llf0Nj+ReQw==", + "license": "MIT", + "dependencies": { + "@types/chai": "^4.2.14", + "@types/mocha": "^8.2.0" + } + }, + "node_modules/markdown-to-text/node_modules/@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==", + "license": "MIT" + }, + "node_modules/markdown-to-text/node_modules/@types/mocha": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==", + "license": "MIT" + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.4" + } + }, "node_modules/mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -1374,31 +1748,32 @@ } }, "node_modules/mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "license": "MIT", "dependencies": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "bin": { @@ -1406,51 +1781,63 @@ "mocha": "bin/mocha.js" }, "engines": { - "node": ">= 14.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/mocha/node_modules/brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, - "node_modules/mocha/node_modules/glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "node_modules/mocha/node_modules/chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, - "license": "ISC", + "license": "MIT", "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "readdirp": "^4.0.1" }, "engines": { - "node": ">=12" + "node": ">= 14.16.0" }, "funding": { - "url": "https://github.com/sponsors/isaacs" + "url": "https://paulmillr.com/funding/" } }, "node_modules/mocha/node_modules/minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { - "node": ">=10" + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/node_modules/readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 14.18.0" + }, + "funding": { + "type": "individual", + "url": "https://paulmillr.com/funding/" } }, "node_modules/mocha/node_modules/supports-color": { @@ -1458,6 +1845,7 @@ "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, + "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, @@ -1516,6 +1904,7 @@ "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, + "license": "MIT", "engines": { "node": ">=0.10.0" } @@ -1528,15 +1917,6 @@ "node": ">= 6" } }, - "node_modules/once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "dependencies": { - "wrappy": "1" - } - }, "node_modules/one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -1550,6 +1930,7 @@ "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, + "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, @@ -1565,6 +1946,7 @@ "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, + "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, @@ -1600,6 +1982,7 @@ "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, + "license": "MIT", "engines": { "node": ">=8" } @@ -1644,11 +2027,19 @@ "node": "*" } }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true, + "license": "ISC" + }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, + "license": "MIT", "engines": { "node": ">=8.6" }, @@ -1656,21 +2047,6 @@ "url": "https://github.com/sponsors/jonschlinkert" } }, - "node_modules/prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true, - "bin": { - "prettier": "bin/prettier.cjs" - }, - "engines": { - "node": ">=14" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -1713,6 +2089,7 @@ "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, + "license": "MIT", "dependencies": { "picomatch": "^2.2.1" }, @@ -1904,6 +2281,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" }, @@ -1912,15 +2290,71 @@ } }, "node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" + } + }, + "node_modules/tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/webpack" + } + }, + "node_modules/test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, + "license": "ISC", "dependencies": { - "has-flag": "^4.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" }, "engines": { - "node": ">=8" + "node": ">=18" + } + }, + "node_modules/test-exclude/node_modules/brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/test-exclude/node_modules/minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": ">=16 || 14 >=14.17" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, "node_modules/text-hex": { @@ -1947,9 +2381,13 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "node_modules/triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==", + "license": "MIT", + "engines": { + "node": ">= 14.0.0" + } }, "node_modules/tslib": { "version": "2.4.0", @@ -1992,6 +2430,21 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "node_modules/v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "license": "ISC", + "dependencies": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + }, + "engines": { + "node": ">=10.12.0" + } + }, "node_modules/vscode-jsonrpc": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", @@ -2069,6 +2522,7 @@ "version": "3.8.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "peer": true, "dependencies": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", @@ -2103,22 +2557,41 @@ } }, "node_modules/winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", + "license": "MIT", "dependencies": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "logform": "^2.7.0", + "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, "engines": { - "node": ">= 6.4.0" + "node": ">= 12.0.0" + } + }, + "node_modules/winston-transport-vscode": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/winston-transport-vscode/-/winston-transport-vscode-0.1.0.tgz", + "integrity": "sha512-iHJEJP5vinrVxMId+gWN00kUR4haP+VMY5pydB53Ug3ZyXZ0EmWZp46hFJQtFQGP+BnxdCA0UElLjUz8QQPEGw==", + "license": "Apache-2.0", + "dependencies": { + "logform": "^2.6.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + }, + "engines": { + "node": ">=18.0.0" + }, + "peerDependencies": { + "winston": ">=3.0.0" } }, "node_modules/winston-transport/node_modules/readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "license": "MIT", "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -2142,9 +2615,9 @@ } }, "node_modules/workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true, "license": "Apache-2.0" }, @@ -2153,6 +2626,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", @@ -2183,12 +2657,6 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -2204,31 +2672,32 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "node_modules/yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "version": "17.7.2", + "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", + "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, + "license": "MIT", "dependencies": { - "cliui": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" }, "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "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, "license": "ISC", "engines": { - "node": ">=10" + "node": ">=12" } }, "node_modules/yargs-unparser": { @@ -2236,6 +2705,7 @@ "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, + "license": "MIT", "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", @@ -2251,6 +2721,7 @@ "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, + "license": "MIT", "engines": { "node": ">=10" }, @@ -2260,10 +2731,16 @@ } }, "dependencies": { + "@bcoe/v8-coverage": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-1.0.2.tgz", + "integrity": "sha512-6zABk/ECA/QYSCQ1NGiVwwbQerUCZ+TQbp64Q3AgmfNvurHH0j8TtXa1qbShXA6qqkpAj4V5W8pP6mLe1mcMqA==", + "dev": true + }, "@colors/colors": { - "version": "1.5.0", - "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.5.0.tgz", - "integrity": "sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==" + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/@colors/colors/-/colors-1.6.0.tgz", + "integrity": "sha512-Ir+AOibqzrIsL6ajt3Rz3LskB7OiMVHqltZmspbW/TJuTVuyOMirVqAkjfY6JISiLHgyNqicAC8AyHHGzNd/dA==" }, "@dabh/diagnostics": { "version": "2.0.3", @@ -2333,6 +2810,34 @@ } } }, + "@istanbuljs/schema": { + "version": "0.1.3", + "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", + "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", + "dev": true + }, + "@jridgewell/resolve-uri": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", + "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", + "dev": true + }, + "@jridgewell/sourcemap-codec": { + "version": "1.5.5", + "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.5.tgz", + "integrity": "sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==", + "dev": true + }, + "@jridgewell/trace-mapping": { + "version": "0.3.31", + "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.31.tgz", + "integrity": "sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==", + "dev": true, + "requires": { + "@jridgewell/resolve-uri": "^3.1.0", + "@jridgewell/sourcemap-codec": "^1.4.14" + } + }, "@pkgjs/parseargs": { "version": "0.11.0", "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", @@ -2384,6 +2889,12 @@ "@types/node": "*" } }, + "@types/istanbul-lib-coverage": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", + "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", + "dev": true + }, "@types/lodash": { "version": "4.14.183", "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.183.tgz", @@ -2418,9 +2929,14 @@ "dev": true, "requires": { "@types/node": "*", - "form-data": "^3.0.0" + "form-data": "^4.0.5" } }, + "@types/triple-beam": { + "version": "1.3.5", + "resolved": "https://registry.npmjs.org/@types/triple-beam/-/triple-beam-1.3.5.tgz", + "integrity": "sha512-6WaYesThRMCl19iryMYP7/x2OVgCtbIVflDGFpWnb9irXI3UjYE4AzmYuiUKY1AJstGijoY+MgUszMgRxIYTYw==" + }, "@types/vscode": { "version": "1.85.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.85.0.tgz", @@ -2434,17 +2950,19 @@ "dev": true }, "@vscode/test-cli": { - "version": "0.0.4", - "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.4.tgz", - "integrity": "sha512-Tx0tfbxeSb2Xlo+jpd+GJrNLgKQHobhRHrYvOipZRZQYWZ82sKiK02VY09UjU1Czc/YnZnqyAnjUfaVGl3h09w==", + "version": "0.0.12", + "resolved": "https://registry.npmjs.org/@vscode/test-cli/-/test-cli-0.0.12.tgz", + "integrity": "sha512-iYN0fDg29+a2Xelle/Y56Xvv7Nc8Thzq4VwpzAF/SIE6918rDicqfsQxV6w1ttr2+SOm+10laGuY9FG2ptEKsQ==", "dev": true, "requires": { - "@types/mocha": "^10.0.2", - "chokidar": "^3.5.3", + "@types/mocha": "^10.0.10", + "c8": "^10.1.3", + "chokidar": "^3.6.0", + "enhanced-resolve": "^5.18.3", "glob": "^10.3.10", "minimatch": "^9.0.3", - "mocha": "^10.2.0", - "supports-color": "^9.4.0", + "mocha": "^11.7.4", + "supports-color": "^10.2.2", "yargs": "^17.7.2" }, "dependencies": { @@ -2465,46 +2983,6 @@ "requires": { "brace-expansion": "^2.0.1" } - }, - "supports-color": { - "version": "9.4.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-9.4.0.tgz", - "integrity": "sha512-VL+lNrEoIXww1coLPOmiEmK/0sGigko5COxI09KzHc2VJXJsQ37UaQ+8quuxjDeA7+KnLGTWRyOXSLLR2Wb4jw==", - "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" - }, - "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" - } - }, - "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 - } - } } } }, @@ -2529,12 +3007,6 @@ "debug": "4" } }, - "ansi-colors": { - "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-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", @@ -2549,9 +3021,9 @@ } }, "anymatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", - "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", + "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "requires": { "normalize-path": "^3.0.0", @@ -2587,9 +3059,9 @@ "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, "binary-extensions": { - "version": "2.2.0", - "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.2.0.tgz", - "integrity": "sha512-jDctJ/IVQbZoJykoeHbhXpOlNBqGNcwXJKJog42E5HDPUwQTSdjCHdihjj0DlnheQ7blbT6dHOafNAiS8ooQKA==", + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/binary-extensions/-/binary-extensions-2.3.0.tgz", + "integrity": "sha512-Ceh+7ox5qe7LJuLHoY0feh3pHuUDHAcRUeyL2VYghZwfpkNIy/+8Ocg0a3UuSoYzavmylwuLWQOf3hl0jjMMIw==", "dev": true }, "brace-expansion": { @@ -2616,6 +3088,35 @@ "integrity": "sha512-qhAVI1+Av2X7qelOfAIYwXONood6XlZE/fXaBSmW/T5SzLAmCgzi+eiWE7fUvbHaeNBQH13UftjpXxsfLkMpgw==", "dev": true }, + "c8": { + "version": "10.1.3", + "resolved": "https://registry.npmjs.org/c8/-/c8-10.1.3.tgz", + "integrity": "sha512-LvcyrOAaOnrrlMpW22n690PUvxiq4Uf9WMhQwNJ9vgagkL/ph1+D4uvjvDA5XCbykrc0sx+ay6pVi9YZ1GnhyA==", + "dev": true, + "requires": { + "@bcoe/v8-coverage": "^1.0.1", + "@istanbuljs/schema": "^0.1.3", + "find-up": "^5.0.0", + "foreground-child": "^3.1.1", + "istanbul-lib-coverage": "^3.2.0", + "istanbul-lib-report": "^3.0.1", + "istanbul-reports": "^3.1.6", + "test-exclude": "^7.0.1", + "v8-to-istanbul": "^9.0.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1" + } + }, + "call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + } + }, "camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", @@ -2645,6 +3146,17 @@ "requires": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } } }, "check-error": { @@ -2657,9 +3169,9 @@ } }, "chokidar": { - "version": "3.5.3", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.3.tgz", - "integrity": "sha512-Dr3sfKRP6oTcjf2JmUmFJfeVMvXBdegxB0iVQ5eb2V10uFJUCAS8OByZdVAyVb8xXNz3GjjTgj9kLWsZTqE6kw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "requires": { "anymatch": "~3.1.2", @@ -2670,27 +3182,16 @@ "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.6.0" - }, - "dependencies": { - "glob-parent": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", - "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", - "dev": true, - "requires": { - "is-glob": "^4.0.1" - } - } } }, "cliui": { - "version": "7.0.4", - "resolved": "https://registry.npmjs.org/cliui/-/cliui-7.0.4.tgz", - "integrity": "sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==", + "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.0", + "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" } }, @@ -2765,6 +3266,12 @@ "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==" }, + "convert-source-map": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", + "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", + "dev": true + }, "core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", @@ -2812,11 +3319,22 @@ "dev": true }, "diff": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", - "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-7.0.0.tgz", + "integrity": "sha512-PJWHUb1RFevKCwaFA9RlG5tCd+FO5iRh9A8HEtkmBH2Li03iJriB6m6JIN4rGz3K3JLawI7/veA1xzRKP6ISBw==", "dev": true }, + "dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + } + }, "eastasianwidth": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", @@ -2832,6 +3350,49 @@ "resolved": "https://registry.npmjs.org/enabled/-/enabled-2.0.0.tgz", "integrity": "sha512-AKrN98kuwOzMIdAizXGI86UFBoo26CL21UM763y1h/GMSJ4/OHU9k2YlsmBpyScFo/wbLzWQJBMCW4+IO3/+OQ==" }, + "enhanced-resolve": { + "version": "5.19.0", + "resolved": "https://registry.npmjs.org/enhanced-resolve/-/enhanced-resolve-5.19.0.tgz", + "integrity": "sha512-phv3E1Xl4tQOShqSte26C7Fl84EwUdZsyOuSSk9qtAGyyQs2s3jJzComh+Abf4g187lUUAvH+H26omrqia2aGg==", + "dev": true, + "requires": { + "graceful-fs": "^4.2.4", + "tapable": "^2.3.0" + } + }, + "es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true + }, + "es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true + }, + "es-object-atoms": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", + "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0" + } + }, + "es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "requires": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + } + }, "escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", @@ -2874,22 +3435,6 @@ "to-regex-range": "^5.0.1" } }, - "find-java-home": { - "version": "1.2.2", - "resolved": "https://registry.npmjs.org/find-java-home/-/find-java-home-1.2.2.tgz", - "integrity": "sha512-rN2LcQa+YDwfnPT0TQgn015eYqJkn3h3G/uVX5oOD2Ge7gKOuoJrntAJO4BiGb+K6lo6Mb+xJdoqbfzqaC75gw==", - "requires": { - "which": "~1.0.5", - "winreg": "~1.2.2" - }, - "dependencies": { - "which": { - "version": "1.0.9", - "resolved": "https://registry.npmjs.org/which/-/which-1.0.9.tgz", - "integrity": "sha512-E87fdQ/eRJr9W1X4wTPejNy9zTW3FI2vpCZSJ/HAY+TkjKVC0TUm1jk6vn2Z7qay0DQy0+RBGdXxj+RmmiGZKQ==" - } - } - }, "find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", @@ -2921,13 +3466,15 @@ } }, "form-data": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", - "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "version": "4.0.5", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", + "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "requires": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.2", "mime-types": "^2.1.12" } }, @@ -2941,19 +3488,19 @@ "universalify": "^0.1.0" } }, - "fs.realpath": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", - "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", - "dev": true - }, "fsevents": { - "version": "2.3.2", - "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", - "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", + "version": "2.3.3", + "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", + "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "optional": true }, + "function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true + }, "get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2966,6 +3513,34 @@ "integrity": "sha512-8vXOvuE167CtIc3OyItco7N/dpRtBbYOsPsXCz7X/PMnlGjYjSGuZJgM1Y7mmew7BKf9BqvLX2tnOVy1BBUsxQ==", "dev": true }, + "get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "requires": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + } + }, + "get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "requires": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + } + }, "glob": { "version": "10.5.0", "resolved": "https://registry.npmjs.org/glob/-/glob-10.5.0.tgz", @@ -2997,6 +3572,21 @@ } } }, + "glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "requires": { + "is-glob": "^4.0.1" + } + }, + "gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true + }, "graceful-fs": { "version": "4.2.10", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.10.tgz", @@ -3008,6 +3598,30 @@ "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", "dev": true }, + "has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true + }, + "has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "requires": { + "has-symbols": "^1.0.3" + } + }, + "hasown": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", + "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", + "dev": true, + "requires": { + "function-bind": "^1.1.2" + } + }, "he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -3022,6 +3636,12 @@ "parse-passwd": "^1.0.0" } }, + "html-escaper": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", + "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", + "dev": true + }, "http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", @@ -3049,16 +3669,6 @@ "integrity": "sha512-XXOFtyqDjNDAQxVfYxuF7g9Il/IbWmmlQg2MYKOH8ExIT1qg6xc4zyS3HaEEATgs1btfzxq15ciUiY7gjSXRGQ==", "dev": true }, - "inflight": { - "version": "1.0.6", - "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", - "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", - "dev": true, - "requires": { - "once": "^1.3.0", - "wrappy": "1" - } - }, "inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", @@ -3104,6 +3714,12 @@ "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", "dev": true }, + "is-path-inside": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", + "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", + "dev": true + }, "is-plain-obj": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-plain-obj/-/is-plain-obj-2.1.0.tgz", @@ -3132,6 +3748,44 @@ "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==" }, + "istanbul-lib-coverage": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", + "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", + "dev": true + }, + "istanbul-lib-report": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", + "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", + "dev": true, + "requires": { + "istanbul-lib-coverage": "^3.0.0", + "make-dir": "^4.0.0", + "supports-color": "^7.1.0" + }, + "dependencies": { + "supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "requires": { + "has-flag": "^4.0.0" + } + } + } + }, + "istanbul-reports": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.2.0.tgz", + "integrity": "sha512-HGYWWS/ehqTV3xN10i23tkPkpH46MLCIMFNCaaKNavAXTF1RkqxawEPtnjnGZ6XKSInBKkiOA5BKS+aZiY3AvA==", + "dev": true, + "requires": { + "html-escaper": "^2.0.0", + "istanbul-lib-report": "^3.0.0" + } + }, "jackspeak": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-3.4.3.tgz", @@ -3142,9 +3796,9 @@ } }, "js-yaml": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", - "integrity": "sha512-wpxZs9NoxZaJESJGIZTyDEaYpl0FKSA+FB9aJiyemKhMwkxQg63h4T1KJgUGHpTqPDNRcmmYLugrRjJlBtWvRA==", + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", + "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "requires": { "argparse": "^2.0.1" @@ -3209,11 +3863,12 @@ } }, "logform": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/logform/-/logform-2.4.2.tgz", - "integrity": "sha512-W4c9himeAwXEdZ05dQNerhFz2XG80P9Oj0loPUMV23VC2it0orMHQhJm4hdnnor3rd1HsGf6a2lPwBM1zeXHGw==", + "version": "2.7.0", + "resolved": "https://registry.npmjs.org/logform/-/logform-2.7.0.tgz", + "integrity": "sha512-TFYA4jnP7PVbmlBIfhlSe+WKxs9dklXMTEGcBCIvLhE/Tn3H6Gk1norupVW7m5Cnd4bLcr08AytbyV/xj7f/kQ==", "requires": { - "@colors/colors": "1.5.0", + "@colors/colors": "1.6.0", + "@types/triple-beam": "^1.3.2", "fecha": "^4.2.0", "ms": "^2.1.1", "safe-stable-stringify": "^2.3.1", @@ -3237,6 +3892,42 @@ "yallist": "^4.0.0" } }, + "make-dir": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", + "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", + "dev": true, + "requires": { + "semver": "^7.5.3" + } + }, + "markdown-to-text": { + "version": "0.1.1", + "resolved": "https://registry.npmjs.org/markdown-to-text/-/markdown-to-text-0.1.1.tgz", + "integrity": "sha512-co/J5l8mJ2RK9wD/nQRGwO7JxoeyfvVNtOZll016EdAX2qYkwCWMdtYvJO42b41Ho7GFEJMuly9llf0Nj+ReQw==", + "requires": { + "@types/chai": "^4.2.14", + "@types/mocha": "^8.2.0" + }, + "dependencies": { + "@types/chai": { + "version": "4.3.20", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.3.20.tgz", + "integrity": "sha512-/pC9HAB5I/xMlc5FP77qjCnI16ChlJfW0tGa0IUcFn38VJrTV6DeZ60NU5KZBtaOZqjdpwTWohz5HU1RrhiYxQ==" + }, + "@types/mocha": { + "version": "8.2.3", + "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", + "integrity": "sha512-ekGvFhFgrc2zYQoX4JeZPmVzZxw6Dtllga7iGHzfbYIYkAMUx/sAFP2GdFpLff+vdHXu5fl7WX9AT+TtqYcsyw==" + } + } + }, + "math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true + }, "mime-db": { "version": "1.52.0", "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", @@ -3266,64 +3957,67 @@ "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==" }, "mocha": { - "version": "10.8.2", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.8.2.tgz", - "integrity": "sha512-VZlYo/WE8t1tstuRmqgeyBgCbJc/lEdopaa+axcKzTBJ+UIdlAB9XnmvTCAH4pwR4ElNInaedhEBmZD8iCSVEg==", + "version": "11.7.5", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-11.7.5.tgz", + "integrity": "sha512-mTT6RgopEYABzXWFx+GcJ+ZQ32kp4fMf0xvpZIIfSq9Z8lC/++MtcCnQ9t5FP2veYEP95FIYSvW+U9fV4xrlig==", "dev": true, "requires": { - "ansi-colors": "^4.1.3", "browser-stdout": "^1.3.1", - "chokidar": "^3.5.3", + "chokidar": "^4.0.1", "debug": "^4.3.5", - "diff": "^5.2.0", + "diff": "^7.0.0", "escape-string-regexp": "^4.0.0", "find-up": "^5.0.0", - "glob": "^8.1.0", + "glob": "^10.4.5", "he": "^1.2.0", + "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "log-symbols": "^4.1.0", - "minimatch": "^5.1.6", + "minimatch": "^9.0.5", "ms": "^2.1.3", + "picocolors": "^1.1.1", "serialize-javascript": "^6.0.2", "strip-json-comments": "^3.1.1", "supports-color": "^8.1.1", - "workerpool": "^6.5.1", - "yargs": "^16.2.0", - "yargs-parser": "^20.2.9", + "workerpool": "^9.2.0", + "yargs": "^17.7.2", + "yargs-parser": "^21.1.1", "yargs-unparser": "^2.0.0" }, "dependencies": { "brace-expansion": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.1.tgz", - "integrity": "sha512-XnAIvQ8eM+kC6aULx6wuQiwVsnzsi9d3WxzV3FpWTGA19F621kwdbsAcFKXgKUHZWsy+mY6iL1sHTxWEFCytDA==", + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "requires": { "balanced-match": "^1.0.0" } }, - "glob": { - "version": "8.1.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-8.1.0.tgz", - "integrity": "sha512-r8hpEjiQEYlF2QU0df3dS+nxxSIreXQS1qRhMJM0Q5NDdR386C7jb7Hwwod8Fgiuex+k0GFjgft18yvxm5XoCQ==", + "chokidar": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-4.0.3.tgz", + "integrity": "sha512-Qgzu8kfBvo+cA4962jnP1KkS6Dop5NS6g7R5LFYJr4b8Ub94PPQXUksCw9PvXoeXPRRddRNC5C1JQUR2SMGtnA==", "dev": true, "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^5.0.1", - "once": "^1.3.0" + "readdirp": "^4.0.1" } }, "minimatch": { - "version": "5.1.6", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", - "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", "dev": true, "requires": { "brace-expansion": "^2.0.1" } }, + "readdirp": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-4.1.2.tgz", + "integrity": "sha512-GDhwkLfywWL2s6vEjyhri+eXmfH6j1L7JE27WhqLeYzoh/A3DBaYGEj2H/HFZCn/kMfim73FXxEJTw06WtxQwg==", + "dev": true + }, "supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -3370,15 +4064,6 @@ "resolved": "https://registry.npmjs.org/object-hash/-/object-hash-2.2.0.tgz", "integrity": "sha512-gScRMn0bS5fH+IuwyIFgnh9zBdo4DV+6GhygmWM9HyNJSgS0hScp1f5vjtm7oIIOiT9trXrShAkLFSc2IqKNgw==" }, - "once": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", - "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", - "dev": true, - "requires": { - "wrappy": "1" - } - }, "one-time": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/one-time/-/one-time-1.0.0.tgz", @@ -3454,18 +4139,18 @@ "integrity": "sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==", "dev": true }, + "picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, "picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true }, - "prettier": { - "version": "3.3.3", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", - "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", - "dev": true - }, "process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", @@ -3642,12 +4327,46 @@ "dev": true }, "supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-10.2.2.tgz", + "integrity": "sha512-SS+jx45GF1QjgEXQx4NJZV9ImqmO2NPz5FNsIHrsDjh2YsHnawpan7SNQ1o8NuhrbHZy9AZhIoCUiCeaW/C80g==", + "dev": true + }, + "tapable": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/tapable/-/tapable-2.3.0.tgz", + "integrity": "sha512-g9ljZiwki/LfxmQADO3dEY1CbpmXT5Hm2fJ+QaGKwSXUylMybePR7/67YW7jOrrvjEgL1Fmz5kzyAjWVWLlucg==", + "dev": true + }, + "test-exclude": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-7.0.1.tgz", + "integrity": "sha512-pFYqmTw68LXVjeWJMST4+borgQP2AyMNbg1BpZh9LbyhUeNkeaPF9gzfPGUAnSMV3qPYdWUwDIjjCLiSDOl7vg==", "dev": true, "requires": { - "has-flag": "^4.0.0" + "@istanbuljs/schema": "^0.1.2", + "glob": "^10.4.1", + "minimatch": "^9.0.4" + }, + "dependencies": { + "brace-expansion": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", + "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", + "dev": true, + "requires": { + "balanced-match": "^1.0.0" + } + }, + "minimatch": { + "version": "9.0.5", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.5.tgz", + "integrity": "sha512-G6T0ZX48xgozx7587koeX9Ys2NYy6Gmv//P89sEte9V9whIapMNF4idKxnW2QtCcLiTWlb/wfCabAtAFWhhBow==", + "dev": true, + "requires": { + "brace-expansion": "^2.0.1" + } + } } }, "text-hex": { @@ -3670,9 +4389,9 @@ "integrity": "sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==" }, "triple-beam": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.3.0.tgz", - "integrity": "sha512-XrHUvV5HpdLmIj4uVMxHggLbFSZYIn7HEWsqePZcI50pco+MPqJ50wMGY794X7AOOhxOBAjbkqfAbEe/QMp2Lw==" + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/triple-beam/-/triple-beam-1.4.1.tgz", + "integrity": "sha512-aZbgViZrg1QNcG+LULa7nhZpJTZSLm/mXnHXnbAbjmN5aSa0y7V+wvv6+4WaBtpISJzThKy+PIPxc1Nq1EJ9mg==" }, "tslib": { "version": "2.4.0", @@ -3701,6 +4420,17 @@ "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==" }, + "v8-to-istanbul": { + "version": "9.3.0", + "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", + "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", + "dev": true, + "requires": { + "@jridgewell/trace-mapping": "^0.3.12", + "@types/istanbul-lib-coverage": "^2.0.1", + "convert-source-map": "^2.0.0" + } + }, "vscode-jsonrpc": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-6.0.0.tgz", @@ -3766,6 +4496,7 @@ "version": "3.8.1", "resolved": "https://registry.npmjs.org/winston/-/winston-3.8.1.tgz", "integrity": "sha512-r+6YAiCR4uI3N8eQNOg8k3P3PqwAm20cLKlzVD9E66Ch39+LZC+VH1UKf9JemQj2B3QoUHfKD7Poewn0Pr3Y1w==", + "peer": true, "requires": { "@dabh/diagnostics": "^2.0.2", "async": "^3.2.3", @@ -3803,19 +4534,19 @@ } }, "winston-transport": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.5.0.tgz", - "integrity": "sha512-YpZzcUzBedhlTAfJg6vJDlyEai/IFMIVcaEZZyl3UXIl4gmqRpU7AE89AHLkbzLUsv0NVmw7ts+iztqKxxPW1Q==", + "version": "4.9.0", + "resolved": "https://registry.npmjs.org/winston-transport/-/winston-transport-4.9.0.tgz", + "integrity": "sha512-8drMJ4rkgaPo1Me4zD/3WLfI/zPdA9o2IipKODunnGDcuqbHwjsbB79ylv04LCGGzU0xQ6vTznOMpQGaLhhm6A==", "requires": { - "logform": "^2.3.2", - "readable-stream": "^3.6.0", + "logform": "^2.7.0", + "readable-stream": "^3.6.2", "triple-beam": "^1.3.0" }, "dependencies": { "readable-stream": { - "version": "3.6.0", - "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.0.tgz", - "integrity": "sha512-BViHy7LKeTz4oNnkcLJ+lVSL6vpiFeX6/d3oSH8zCW7UxP2onchk+vTGB143xuFjHS3deTgkKoXXymXqymiIdA==", + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", "requires": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", @@ -3824,10 +4555,20 @@ } } }, + "winston-transport-vscode": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/winston-transport-vscode/-/winston-transport-vscode-0.1.0.tgz", + "integrity": "sha512-iHJEJP5vinrVxMId+gWN00kUR4haP+VMY5pydB53Ug3ZyXZ0EmWZp46hFJQtFQGP+BnxdCA0UElLjUz8QQPEGw==", + "requires": { + "logform": "^2.6.0", + "triple-beam": "^1.4.1", + "winston-transport": "^4.7.0" + } + }, "workerpool": { - "version": "6.5.1", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", - "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", + "version": "9.3.4", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-9.3.4.tgz", + "integrity": "sha512-TmPRQYYSAnnDiEB0P/Ytip7bFGvqnSU6I2BcuSw7Hx+JSg/DsUi5ebYfc8GYaSdpuvOcEs6dXxPurOYpe9QFwg==", "dev": true }, "wrap-ansi": { @@ -3851,12 +4592,6 @@ "strip-ansi": "^6.0.0" } }, - "wrappy": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", - "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", - "dev": true - }, "y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", @@ -3869,24 +4604,24 @@ "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==" }, "yargs": { - "version": "16.2.0", - "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", - "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", + "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": "^7.0.2", + "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", - "string-width": "^4.2.0", + "string-width": "^4.2.3", "y18n": "^5.0.5", - "yargs-parser": "^20.2.2" + "yargs-parser": "^21.1.1" } }, "yargs-parser": { - "version": "20.2.9", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", - "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", + "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 }, "yargs-unparser": { diff --git a/package.json b/package.json index 933198a..b17524c 100644 --- a/package.json +++ b/package.json @@ -36,6 +36,9 @@ "Snippets" ], "main": "./dist/extension", + "activationEvents": [ + "workspaceContains:**/*.cql" + ], "contributes": { "languages": [ { @@ -97,31 +100,71 @@ "command": "cql.action.executeCql", "title": "Execute CQL", "category": "CQL" + }, + { + "command": "cql.action.executeCql.selectLibraries", + "title": "Execute CQL - Select Libraries", + "category": "CQL" + }, + { + "command": "cql.action.executeCql.selectTestCases", + "title": "Execute CQL - Select Test Cases", + "category": "CQL" } ], "menus": { "editor/context": [ { "command": "cql.action.executeCql", - "when": "editorLangId == cql", + "when": "editorLangId == cql && cql.languageServerReady", "group": "1_cqlactions@1.execute@1" }, + { + "command": "cql.action.executeCql.selectTestCases", + "when": "editorLangId == cql && cql.languageServerReady", + "group": "1_cqlactions@1.execute@3" + }, { "command": "cql.action.viewElm.xml", - "when": "editorLangId == cql", + "when": "editorLangId == cql && cql.languageServerReady", "group": "1_cqlactions@2.elm@1" }, { "command": "cql.action.viewElm.json", - "when": "editorLangId == cql", + "when": "editorLangId == cql && cql.languageServerReady", "group": "1_cqlactions@2.elm@2" } + ], + "commandPalette": [ + { + "command": "cql.action.executeCql", + "when": "false" + }, + { + "command": "cql.action.executeCql.selectTestCases", + "when": "false" + }, + { + "command": "cql.action.viewElm.xml", + "when": "false" + }, + { + "command": "cql.action.viewElm.json", + "when": "false" + }, + { + "command": "cql.action.executeCql.selectLibraries", + "when": "cql.languageServerReady" + } ] } }, "resolutions": { "color-name": "1.1.3" }, + "overrides": { + "form-data": "^4.0.5" + }, "scripts": { "vscode:prepublish": "npm run compile", "compile": "tsc -p ./", @@ -147,27 +190,27 @@ "@types/node-fetch": "^2.6.2", "@types/vscode": "^1.73.0", "@types/winreg": "^1.2.31", - "@vscode/test-cli": "^0.0.4", + "@vscode/test-cli": "^0.0.12", "@vscode/test-electron": "^2.3.6", "chai": "4.5", - "mocha": "^10.2.0", + "mocha": "^11.7.5", "mock-fs": "^5.5.0", - "prettier": "^3.3.3", "typescript": "^5.3.2" }, "dependencies": { "expand-tilde": "^2.0.2", - "find-java-home": "1.2.2", "fs-extra": "^8.1.0", "glob": "^10.5.0", "lodash": "^4.17.23", + "markdown-to-text": "^0.1.1", "node-fetch": "2.6.7", "tslib": "^2.4.0", "vscode-languageclient": "^7.0.0", "vscode-uri": "^3.0.8", "winreg": "^1.2.4", "winston": "^3.8.1", - "winston-daily-rotate-file": "^4.7.1" + "winston-daily-rotate-file": "^4.7.1", + "winston-transport-vscode": "^0.1.0" }, "prettier": { "printWidth": 100, @@ -175,4 +218,4 @@ "tabWidth": 2, "arrowParens": "avoid" } -} \ No newline at end of file +} diff --git a/src/__test__/resources/test-workspace/input/cql/SimpleMeasure.cql b/src/__test__/resources/test-workspace/input/cql/SimpleMeasure.cql new file mode 100644 index 0000000..be23e52 --- /dev/null +++ b/src/__test__/resources/test-workspace/input/cql/SimpleMeasure.cql @@ -0,0 +1,25 @@ +library SimpleMeasure version '1.0.0' + +using FHIR version '4.0.1' +include FHIRHelpers version '4.0.1' called FHIRHelpers + +context Patient + +define "isOfficial": + exists (Patient.name Name where Name.use.value = 'official') + +define "Patient Name": + Coalesce( + "Get Name String"(First(Patient.name Name where Name.use.value = 'official')), + "Get Name String"(First(Patient.name Name where Name.use.value = 'usual')), + "Get Name String"(First(Patient.name)), + 'Unknown' + ) + +define function "Get Name String"(name FHIR.HumanName): + if name is null then null + else + Coalesce( + name.text.value, + Combine(name.given.value, ' ') + ' ' + Coalesce(name.family.value, '') + ) \ No newline at end of file diff --git a/src/__test__/resources/test-workspace/input/cql/cql-options.json b/src/__test__/resources/test-workspace/input/cql/cql-options.json new file mode 100644 index 0000000..5145c34 --- /dev/null +++ b/src/__test__/resources/test-workspace/input/cql/cql-options.json @@ -0,0 +1,19 @@ +{ + "options": [ + "EnableAnnotations", + "EnableResultTypes", + "EnableLocators", + "DisableListDemotion", + "DisableListPromotion" + ], + "formats": [ + "XML", + "JSON" + ], + "validateUnits": true, + "verifyOnly": false, + "errorLevel": "Info", + "signatureLevel": "All", + "analyzeDataRequirements": false, + "collapseDataRequirements": false +} \ No newline at end of file diff --git a/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/MeasureReport-9876.json b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/MeasureReport-9876.json new file mode 100644 index 0000000..354be15 --- /dev/null +++ b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/MeasureReport-9876.json @@ -0,0 +1,45 @@ +{ + "resourceType": "MeasureReport", + "id": "9876", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/test-case-cqfm" + ] + }, + "contained": [ + { + "resourceType": "Parameters", + "id": "6b356ec3-e349-4b6d-8281-10186194ec3c-parameters", + "parameter": [ + { + "name": "subject", + "valueString": "0c76341a-34f1-4d1b-9bbe-5915e00a0818" + } + ] + } + ], + "extension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-testCaseDescription", + "valueMarkdown": "SimpleMeasure" + } + ], + "modifierExtension": [ + { + "url": "http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-isTestCase", + "valueBoolean": true + } + ], + "status": "complete", + "type": "individual", + "measure": "https://vscode-cql/tests/measure/SimpleMeasure", + "period": { + "start": "2026-01-01", + "end": "2026-12-31" + }, + "evaluatedResource": [ + { + "reference": "Patient/1111" + } + ] +} \ No newline at end of file diff --git a/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/Patient-1111.json b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/Patient-1111.json new file mode 100644 index 0000000..d974442 --- /dev/null +++ b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/1111/Patient-1111.json @@ -0,0 +1,168 @@ +{ + "resourceType": "Patient", + "id": "1111", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient" + ] + }, + "text": { + "status": "generated", + "div": "

Jim Chalmers male, DoB: 1974-12-25 ( Medical record number/12345\u00a0(use:\u00a0USUAL,\u00a0period:\u00a02001-05-06 --> (ongoing)))


Active:trueDeceased:false
Alt Names:
  • Peter James Chalmers (OFFICIAL)
  • Peter James Windsor (MAIDEN)
Contact Details:
  • ph: (03) 5555 6473(WORK)
  • ph: (03) 3410 5613(MOBILE)
  • ph: (03) 5555 8834(OLD)
  • 534 Erewhon St PeasantVille, Utah 84414(HOME)
Next-of-Kin:
  • Bénédicte du Marché (female)
  • 534 Erewhon St PleasantVille VT 3999 (HOME)
  • +33 (237) 998327
  • Valid Period: 2012 --> (ongoing)
Links:
" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:1.2.36.146.595.217.0.1", + "value": "12345", + "period": { + "start": "2001-05-06" + }, + "assigner": { + "display": "Acme Healthcare" + } + } + ], + "active": true, + "name": [ + { + "use": "official", + "family": "Chalmers", + "given": [ + "Peter", + "James" + ] + }, + { + "use": "usual", + "family": "Chalmers", + "given": [ + "Jim" + ] + }, + { + "use": "maiden", + "family": "Windsor", + "given": [ + "Peter", + "James" + ], + "period": { + "end": "2002" + } + } + ], + "telecom": [ + { + "system": "phone", + "value": "(03) 5555 6473", + "use": "work", + "rank": 1 + }, + { + "system": "phone", + "value": "(03) 3410 5613", + "use": "mobile", + "rank": 2 + }, + { + "system": "phone", + "value": "(03) 5555 8834", + "use": "old", + "period": { + "end": "2014" + } + } + ], + "gender": "male", + "birthDate": "1974-12-25", + "_birthDate": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime", + "valueDateTime": "1974-12-25T14:35:45-05:00" + } + ] + }, + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "type": "both", + "text": "534 Erewhon St PeasantVille, Utah 84414", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "UT", + "postalCode": "84414", + "period": { + "start": "1974-12-25" + } + } + ], + "contact": [ + { + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "N" + } + ] + } + ], + "name": { + "family": "du Marché", + "_family": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix", + "valueString": "VV" + } + ] + }, + "given": [ + "Bénédicte" + ] + }, + "telecom": [ + { + "system": "phone", + "value": "+33 (237) 998327" + } + ], + "address": { + "use": "home", + "type": "both", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "VT", + "postalCode": "3999", + "period": { + "start": "1974-12-25" + } + }, + "gender": "female", + "period": { + "start": "2012" + } + } + ], + "managingOrganization": { + "reference": "Organization/example" + } +} \ No newline at end of file diff --git a/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/2222/Patient-2222.json b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/2222/Patient-2222.json new file mode 100644 index 0000000..7ccb161 --- /dev/null +++ b/src/__test__/resources/test-workspace/input/tests/measure/SimpleMeasure/2222/Patient-2222.json @@ -0,0 +1,160 @@ +{ + "resourceType": "Patient", + "id": "2222", + "meta": { + "profile": [ + "http://hl7.org/fhir/us/qicore/StructureDefinition/qicore-patient" + ] + }, + "text": { + "status": "generated", + "div": "

Jim Chalmers male, DoB: 1974-12-25 ( Medical record number/12345\u00a0(use:\u00a0USUAL,\u00a0period:\u00a02001-05-06 --> (ongoing)))


Active:trueDeceased:false
Alt Names:
  • Peter James Chalmers (OFFICIAL)
  • Peter James Windsor (MAIDEN)
Contact Details:
  • ph: (03) 5555 6473(WORK)
  • ph: (03) 3410 5613(MOBILE)
  • ph: (03) 5555 8834(OLD)
  • 534 Erewhon St PeasantVille, Utah 84414(HOME)
Next-of-Kin:
  • Bénédicte du Marché (female)
  • 534 Erewhon St PleasantVille VT 3999 (HOME)
  • +33 (237) 998327
  • Valid Period: 2012 --> (ongoing)
Links:
" + }, + "identifier": [ + { + "use": "usual", + "type": { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0203", + "code": "MR" + } + ] + }, + "system": "urn:oid:1.2.36.146.595.217.0.1", + "value": "12345", + "period": { + "start": "2001-05-06" + }, + "assigner": { + "display": "Acme Healthcare" + } + } + ], + "active": true, + "name": [ + { + "use": "usual", + "family": "Chalmers", + "given": [ + "Jim" + ] + }, + { + "use": "maiden", + "family": "Windsor", + "given": [ + "Peter", + "James" + ], + "period": { + "end": "2002" + } + } + ], + "telecom": [ + { + "system": "phone", + "value": "(03) 5555 6473", + "use": "work", + "rank": 1 + }, + { + "system": "phone", + "value": "(03) 3410 5613", + "use": "mobile", + "rank": 2 + }, + { + "system": "phone", + "value": "(03) 5555 8834", + "use": "old", + "period": { + "end": "2014" + } + } + ], + "gender": "male", + "birthDate": "1974-12-25", + "_birthDate": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/patient-birthTime", + "valueDateTime": "1974-12-25T14:35:45-05:00" + } + ] + }, + "deceasedBoolean": false, + "address": [ + { + "use": "home", + "type": "both", + "text": "534 Erewhon St PeasantVille, Utah 84414", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "UT", + "postalCode": "84414", + "period": { + "start": "1974-12-25" + } + } + ], + "contact": [ + { + "relationship": [ + { + "coding": [ + { + "system": "http://terminology.hl7.org/CodeSystem/v2-0131", + "code": "N" + } + ] + } + ], + "name": { + "family": "du Marché", + "_family": { + "extension": [ + { + "url": "http://hl7.org/fhir/StructureDefinition/humanname-own-prefix", + "valueString": "VV" + } + ] + }, + "given": [ + "Bénédicte" + ] + }, + "telecom": [ + { + "system": "phone", + "value": "+33 (237) 998327" + } + ], + "address": { + "use": "home", + "type": "both", + "line": [ + "534 Erewhon St" + ], + "city": "PleasantVille", + "district": "Rainbow", + "state": "VT", + "postalCode": "3999", + "period": { + "start": "1974-12-25" + } + }, + "gender": "female", + "period": { + "start": "2012" + } + } + ], + "managingOrganization": { + "reference": "Organization/example" + } +} \ No newline at end of file diff --git a/src/__test__/suite/extension.test.ts b/src/__test__/suite/extension/extension.test.ts similarity index 100% rename from src/__test__/suite/extension.test.ts rename to src/__test__/suite/extension/extension.test.ts diff --git a/src/__test__/suite/helpers/file-helpers.test.ts b/src/__test__/suite/helpers/file-helpers.test.ts index c1f86b5..ccfa725 100644 --- a/src/__test__/suite/helpers/file-helpers.test.ts +++ b/src/__test__/suite/helpers/file-helpers.test.ts @@ -1,38 +1,33 @@ import { expect } from 'chai'; import * as fs from 'fs'; -import mock from 'mock-fs'; -import { ensureExists } from '../../../helpers/file-helpers'; +import * as os from 'os'; +import * as path from 'path'; +import { ensureExists } from '../../../utils/file-utils'; + +suite('ensureExists()', () => { + let testDir: string; + + setup(() => { + testDir = fs.mkdtempSync(path.join(os.tmpdir(), 'vscode-cql-test-')); + }); -suite('ensureExists() with mock-fs', () => { - teardown(() => { - // Crucial: Restore the real file system after every test - mock.restore(); + fs.rmSync(testDir, { recursive: true, force: true }); }); test('should create the directory if it does not exist', () => { - // Setup an empty virtual file system - mock({}); + const testPath = path.join(testDir, 'new-folder'); - const testPath = './new-folder'; - ensureExists(testPath); - // Verify the folder now exists in the virtual FS - const exists = fs.existsSync(testPath); - expect(exists).to.be.true; + expect(fs.existsSync(testPath)).to.be.true; }); test('should not throw an error if the directory already exists', () => { - // Setup virtual FS with an existing directory - mock({ - './existing-folder': {} - }); - - const testPath = './existing-folder'; - - // This should run without calling mkdirSync again/throwing error + const testPath = path.join(testDir, 'existing-folder'); + fs.mkdirSync(testPath); + expect(() => ensureExists(testPath)).to.not.throw(); expect(fs.existsSync(testPath)).to.be.true; }); -}); \ No newline at end of file +}); diff --git a/src/__test__/suite/index.ts b/src/__test__/suite/index.ts deleted file mode 100644 index f840a66..0000000 --- a/src/__test__/suite/index.ts +++ /dev/null @@ -1,40 +0,0 @@ -import { glob } from 'glob'; // Use named import for glob v10 -import Mocha from 'mocha'; -import * as path from 'path'; - -export async function run(): Promise { - // Create the mocha test - const mocha = new Mocha({ - ui: 'tdd', - timeout: 50000, - }); - - const testsRoot = path.resolve(__dirname, '..'); - - try { - // glob v10 returns a Promise when no callback is provided - const files = await glob('**/*.test.js', { cwd: testsRoot }); - - // Add files to the test suite - files.forEach((f: string) => mocha.addFile(path.resolve(testsRoot, f))); - - return new Promise((resolve, reject) => { - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - reject(new Error(`${failures} tests failed.`)); - } else { - resolve(); - } - }); - } catch (err) { - console.error(err); - reject(err); - } - }); - } catch (err) { - // Catch errors from the glob search - throw err; - } -} \ No newline at end of file diff --git a/src/__test__/suite/model/testCase.getMeasureReportDescription.test.ts b/src/__test__/suite/model/testCase.getMeasureReportDescription.test.ts new file mode 100644 index 0000000..df3e180 --- /dev/null +++ b/src/__test__/suite/model/testCase.getMeasureReportDescription.test.ts @@ -0,0 +1,26 @@ +import { expect } from 'chai'; +import { Uri, workspace } from 'vscode'; +import { getMeasureReportDescription } from '../../../model/testCase'; + +suite('testCase.getMeasureReportDescription tests', () => { + test('should return undefined when directory does not exist', () => { + const folder = '/tests/measure/library-example/1234'; + expect(getMeasureReportDescription(Uri.file(folder))).to.equal(undefined); + }); + + test('should return undefined when directory exist but there is no measure report', () => { + const testCaseFolder = Uri.joinPath( + workspace.workspaceFolders![0].uri, + 'input/tests/measure/SimpleMeasure/2222', + ); + expect(getMeasureReportDescription(testCaseFolder)).to.equal(undefined); + }); + + test('should return description', () => { + const testCaseFolder = Uri.joinPath( + workspace.workspaceFolders![0].uri, + 'input/tests/measure/SimpleMeasure/1111', + ); + expect(getMeasureReportDescription(testCaseFolder)).to.equal('SimpleMeasure'); + }); +}); diff --git a/src/commands.ts b/src/commands.ts deleted file mode 100644 index 1a22497..0000000 --- a/src/commands.ts +++ /dev/null @@ -1,57 +0,0 @@ -/** - * These are constants for the commands used by or implemented by this extension - * Commands that are defined by the vscode api are named accordingly, - * Commands implemented defined by this extension are prefixed "cql" - */ -export namespace Commands { - /** - * Open Browser - */ - export const OPEN_BROWSER = "vscode.open"; - - /** - * Execute Workspace Command - */ - export const EXECUTE_WORKSPACE_COMMAND = "cql.execute.workspaceCommand"; - - /** - * Open CQL Language Server Log file - */ - export const OPEN_SERVER_LOG = "cql.open.serverLog"; - - /** - * Open CQL client Log file - */ - export const OPEN_CLIENT_LOG = "cql.open.clientLog"; - - /** - * Open CQL log files side by side - */ - export const OPEN_LOGS = "cql.open.logs"; - - /* - * Show Output - */ - export const OPEN_OUTPUT = "cql.open.output"; - - /* - * View ELM for CQL - */ - // TODO: Shorten the command, the whole namespace isn't needed. - export const VIEW_ELM_COMMAND_XML = "cql.action.viewElm.xml"; - export const VIEW_ELM_COMMAND_JSON = "cql.action.viewElm.json"; - export const VIEW_ELM = "org.opencds.cqf.cql.ls.viewElm"; - - /* - * Execute CQL - * TODO: Deprecate once full debugging support exists - */ - export const EXECUTE_CQL_COMMAND = "cql.action.executeCql"; - export const EXECUTE_CQL = - "org.opencds.cqf.cql.ls.plugin.debug.startDebugSession"; - - /* - * Open settings.json - */ - export const OPEN_JSON_SETTINGS = "workbench.action.openSettingsJson"; -} diff --git a/src/commands/README.md b/src/commands/README.md new file mode 100644 index 0000000..48e67b6 --- /dev/null +++ b/src/commands/README.md @@ -0,0 +1,11 @@ +# Commands + +The commands modules are meant to be the 'bridge' between the VSCode UI and underlying CQL services. + +Command operations + +- are triggered by users interacting with the VSCode UI. +- are allowed to update the UI as needed + - to inform the user of the operation status + - show result from operations +- can use all windowing/logging capabilities provided by vscode diff --git a/src/commands/commands.ts b/src/commands/commands.ts new file mode 100644 index 0000000..cec9d62 --- /dev/null +++ b/src/commands/commands.ts @@ -0,0 +1,59 @@ +/** + * These are constants for the commands used by or implemented by this extension + * Commands that are defined by the vscode api are named accordingly, + * Commands implemented defined by this extension are prefixed "cql" + */ +export namespace Commands { + /** + * Open Browser + */ + export const OPEN_BROWSER = 'vscode.open'; + + /** + * Execute Workspace Command + */ + export const EXECUTE_WORKSPACE_COMMAND = 'cql.execute.workspaceCommand'; + + /** + * Open CQL Language Server Log file + */ + export const OPEN_SERVER_LOG = 'cql.open.serverLog'; + + /** + * Open CQL client Log file + */ + export const OPEN_CLIENT_LOG = 'cql.open.clientLog'; + + /** + * Open CQL log files side by side + */ + export const OPEN_LOGS = 'cql.open.logs'; + + /* + * Show Output + */ + export const OPEN_OUTPUT = 'cql.open.output'; + + /* + * View ELM for CQL + */ + // TODO: Shorten the command, the whole namespace isn't needed. + export const VIEW_ELM_COMMAND_XML = 'cql.action.viewElm.xml'; + export const VIEW_ELM_COMMAND_JSON = 'cql.action.viewElm.json'; + export const VIEW_ELM = 'org.opencds.cqf.cql.ls.viewElm'; + + /* + * Execute CQL + * TODO: Deprecate once full debugging support exists + */ + export const EXECUTE_CQL_COMMAND = 'cql.action.executeCql'; + export const EXECUTE_CQL = 'org.opencds.cqf.cql.ls.plugin.debug.startDebugSession'; + + export const EXECUTE_CQL_COMMAND_SELECT_LIBRARIES = 'cql.action.executeCql.selectLibraries'; + export const EXECUTE_CQL_COMMAND_SELECT_TEST_CASES = 'cql.action.executeCql.selectTestCases'; + + /* + * Open settings.json + */ + export const OPEN_JSON_SETTINGS = 'workbench.action.openSettingsJson'; +} diff --git a/src/commands/execute-cql.ts b/src/commands/execute-cql.ts new file mode 100644 index 0000000..f7d2b9d --- /dev/null +++ b/src/commands/execute-cql.ts @@ -0,0 +1,345 @@ +import * as fse from 'fs-extra'; +import { glob } from 'glob'; +import markdownToText from 'markdown-to-text'; +import * as fs from 'node:fs'; +import { + commands, + ExtensionContext, + Position, + ProgressLocation, + TextEditor, + Uri, + window, + workspace, +} from 'vscode'; +import { Utils } from 'vscode-uri'; +import { Commands } from '../commands/commands'; +import { executeCql } from '../cql-service/cqlService.executeCql'; +import * as log from '../log-services/logger'; +import { getTestCases, TestCase, TestCaseExclusion } from '../model/testCase'; + +let _context: ExtensionContext | undefined; + +interface CqlPaths { + libraryDirectoryPath: Uri; + projectDirectoryPath: Uri; + optionsPath: Uri; + resultDirectoryPath: Uri; + terminologyDirectoryPath: Uri; + testConfigPath: Uri; + testDirectoryPath: Uri; +} + +interface TestConfig { + testCasesToExclude: TestCaseExclusion[]; +} + +export function register(context: ExtensionContext): void { + _context = context; + context.subscriptions.push( + commands.registerCommand(Commands.EXECUTE_CQL_COMMAND, async (uri: Uri) => { + executeCQLFile(uri); + }), + commands.registerCommand(Commands.EXECUTE_CQL_COMMAND_SELECT_LIBRARIES, async (uri: Uri) => { + selectLibraries(); + }), + commands.registerCommand(Commands.EXECUTE_CQL_COMMAND_SELECT_TEST_CASES, async (uri: Uri) => { + selectTestCases(uri); + }), + ); +} + +export async function selectLibraries(): Promise { + const cqlPaths = getCqlPaths(); + if (!cqlPaths) { + window.showErrorMessage('Unable to determine needed CQL Paths.'); + return; + } + + const libraries = getLibraries(cqlPaths.libraryDirectoryPath); + const quickPickItems = libraries + .map(uri => ({ label: Utils.basename(uri), uri })) + .sort((a, b) => a.label.localeCompare(b.label)); + + const quickPick = window.createQuickPick<{ label: string; uri: Uri }>(); + if (quickPickItems.length > 0) { + quickPick.items = quickPickItems; + quickPick.canSelectMany = true; + + const stateKey = 'selectLibraries.selections'; + const savedSelections = _context?.workspaceState.get(stateKey) ?? []; + if (savedSelections.length > 0) { + quickPick.selectedItems = quickPick.items.filter(item => + savedSelections.includes(item.label), + ); + } + + quickPick.show(); + + quickPick.onDidAccept(async () => { + const selected = [...quickPick.selectedItems]; + _context?.workspaceState.update(stateKey, selected.map(item => item.label)); + quickPick.hide(); + + await window.withProgress( + { + location: ProgressLocation.Notification, + title: 'Executing CQL Libraries', + cancellable: false, + }, + async progress => { + const total = selected.length; + for (let i = 0; i < total; i++) { + const item = selected[i]; + progress.report({ + message: `(${i + 1}/${total}) ${item.label}`, + increment: (1 / total) * 100, + }); + await executeCQLFile(item.uri); + await new Promise(resolve => setTimeout(resolve, 500)); + } + }, + ); + }); + } +} + +export async function selectTestCases(cqlFileUri: Uri): Promise { + const cqlPaths = getCqlPaths(); + if (!cqlPaths) { + const msg = 'Unable to resolve needed CQL project paths.'; + log.error(msg); + window.showErrorMessage(msg); + return; + } + + const quickPick = window.createQuickPick(); + const libraryName = Utils.basename(cqlFileUri).replace('.cql', '').split('-')[0]; + const outputPath = Utils.resolvePath(cqlPaths.resultDirectoryPath, `${libraryName}.txt`); + fse.ensureFileSync(outputPath.fsPath); + const textDocument = await workspace.openTextDocument(outputPath); + await window.showTextDocument(textDocument); + const testConfig = loadTestConfig(cqlPaths.testConfigPath); + const excludedTestCases = getExcludedTestCases(libraryName, testConfig.testCasesToExclude); + const testCases = getTestCases( + cqlPaths.testDirectoryPath, + libraryName, + Array.from(excludedTestCases.keys()), + ); + const namedTestCases = testCases.filter( + (testCase): testCase is Required => testCase.name !== undefined, + ); + const quickPickItems = namedTestCases.map(testCase => ({ + label: testCase.name, + detail: markdownToText(testCase.description), // use quickpick detail to get description on 2nd line of quickpick + })); + + if (quickPickItems.length > 0) { + quickPick.items = quickPickItems; + quickPick.canSelectMany = true; + + const stateKey = `selectTestCases.selections.${libraryName}`; + const savedSelections = _context?.workspaceState.get(stateKey) ?? []; + if (savedSelections.length > 0) { + quickPick.selectedItems = quickPick.items.filter(item => + savedSelections.includes(item.label), + ); + } + + quickPick.show(); + + quickPick.onDidAccept(() => { + const selectedLabels = quickPick.selectedItems.map(item => item.label); + _context?.workspaceState.update(stateKey, selectedLabels); + + const selected = namedTestCases.filter(testCase => + selectedLabels.some(label => label === testCase.name), + ); + quickPick.hide(); + executeCQLFile(cqlFileUri, selected); + }); + } +} + +export async function executeCQLFile( + cqlFileUri: Uri, + testCases: Array | undefined = undefined, +): Promise { + if (!fs.existsSync(cqlFileUri.fsPath)) { + window.showInformationMessage('No library content found. Please save before executing.'); + return; + } + + const cqlPaths = getCqlPaths(); + if (!cqlPaths) { + window.showErrorMessage('Unable to determine needed CQL Paths.'); + return; + } + + const libraryName = Utils.basename(cqlFileUri).replace('.cql', '').split('-')[0]; + const outputPath = Utils.resolvePath(cqlPaths.resultDirectoryPath, `${libraryName}.txt`); + fse.ensureFileSync(outputPath.fsPath); + const textDocument = await workspace.openTextDocument(outputPath); + const textEditor = await window.showTextDocument(textDocument); + + const testConfig = loadTestConfig(cqlPaths.testConfigPath); + const excludedTestCases = getExcludedTestCases(libraryName, testConfig.testCasesToExclude); + + if (!testCases) { + testCases = getTestCases( + cqlPaths.testDirectoryPath, + libraryName, + Array.from(excludedTestCases.keys()), + ); + } + + // We didn't find any test cases, so we'll just execute an empty one + if (testCases.length === 0) { + testCases.push({}); + } + + const cqlMessage = `CQL: ${cqlPaths.libraryDirectoryPath.fsPath}`; + const terminologyMessage = fs.existsSync(cqlPaths.terminologyDirectoryPath.fsPath) + ? `Terminology: ${cqlPaths.terminologyDirectoryPath.fsPath}` + : `No terminology found at ${cqlPaths.terminologyDirectoryPath.fsPath}. Evaluation may fail if terminology is required.`; + + let testMessage = []; + if (testCases.length == 1 && testCases[0].name === null) { + testMessage.push( + `No data found at ${cqlPaths.testDirectoryPath.fsPath}. Evaluation may fail if data is required.`, + ); + } else { + testMessage.push(`Test cases:`); + for (let p of testCases) { + testMessage.push(`${p.name} - ${p.path?.fsPath}`); + } + } + + if (excludedTestCases.size > 0) { + testMessage.push('\nExcluded test cases:'); + for (const [testCase, reason] of excludedTestCases.entries()) { + testMessage.push(`${testCase} - ${reason}`); + } + } + + await insertLineAtEnd(textEditor, `${cqlMessage}`); + await insertLineAtEnd(textEditor, `${terminologyMessage}`); + await insertLineAtEnd(textEditor, `${testMessage.join('\n')}\n`); + + const startExecution = Date.now(); + + let fhirVersion = getFhirVersion(); + if (!fhirVersion) { + fhirVersion = 'R4'; + window.showInformationMessage('Unable to determine version of FHIR used. Defaulting to R4.'); + } + + const result: string | undefined = await executeCql( + cqlFileUri, + testCases, + cqlPaths.terminologyDirectoryPath, + fhirVersion, + cqlPaths.optionsPath, + cqlPaths.projectDirectoryPath, + ); + + const endExecution = Date.now(); + + await insertLineAtEnd(textEditor, result!); + await insertLineAtEnd( + textEditor, + `elapsed: ${((endExecution - startExecution) / 1000).toString()} seconds`, + ); +} + +function getCqlPaths(): CqlPaths | undefined { + const projectDirectoryPath = getWorkspacePath(); //workspace.getWorkspaceFolder(cqlFileUri)!.uri; + if (!projectDirectoryPath) { + window.showErrorMessage('Unable to determine path to project root.'); + return; + } + const libraryDirectoryPath = Utils.resolvePath(projectDirectoryPath, 'input', 'cql'); + const testDirectoryPath = Utils.resolvePath(projectDirectoryPath, 'input', 'tests'); + return { + projectDirectoryPath: projectDirectoryPath, + libraryDirectoryPath: libraryDirectoryPath, + optionsPath: Utils.resolvePath(libraryDirectoryPath, 'cql-options.json'), + resultDirectoryPath: Utils.resolvePath(testDirectoryPath, 'results'), + terminologyDirectoryPath: Utils.resolvePath( + projectDirectoryPath, + 'input', + 'vocabulary', + 'valueset', + ), + testConfigPath: Utils.resolvePath(testDirectoryPath, 'config.json'), + testDirectoryPath: testDirectoryPath, + }; +} + +function getExcludedTestCases( + libraryName: string, + testCasesToExclude: TestCaseExclusion[], +): Map { + let excludedTestCases = new Map(); + for (let excludedTestCase of testCasesToExclude) { + if (excludedTestCase.library == libraryName) { + excludedTestCases.set(excludedTestCase.testCase, excludedTestCase.reason); + } + } + return excludedTestCases; +} + +function getFhirVersion(): string | null { + const fhirVersionRegex = /using (FHIR|"FHIR") version '(\d(.|\d)*)'/; + const matches = window.activeTextEditor!.document.getText().match(fhirVersionRegex); + if (matches && matches.length > 2) { + const version = matches[2]; + if (version.startsWith('2')) { + return 'DSTU2'; + } else if (version.startsWith('3')) { + return 'DSTU3'; + } else if (version.startsWith('4')) { + return 'R4'; + } else if (version.startsWith('5')) { + return 'R5'; + } + } + + return null; +} + +function getLibraries(libraryPath: Uri): Array { + if (!fs.existsSync(libraryPath.fsPath)) { + log.warn(`unable to find libraries @ ${libraryPath.fsPath}`); + return []; + } + return glob + .sync(libraryPath.fsPath + `/**/*.cql`) + .filter(f => fs.statSync(f).isFile()) + .map(f => Uri.file(f)); +} + +function getWorkspacePath(): Uri | undefined { + if (workspace.workspaceFolders && workspace.workspaceFolders.length > 0) { + return workspace.workspaceFolders[0].uri; + } + return undefined; +} + +async function insertLineAtEnd(textEditor: TextEditor, text: string) { + const document = textEditor.document; + await textEditor.edit(editBuilder => { + editBuilder.insert(new Position(document.lineCount, 0), text + '\n'); + }); +} + +function loadTestConfig(testConfigPath: Uri): TestConfig { + try { + const jsonString = fs.readFileSync(testConfigPath.fsPath, 'utf-8'); + // Cast the parsed object to the User interface + return JSON.parse(jsonString) as TestConfig; + } catch (error) { + log.error('Error reading/parsing config file', error); + return { testCasesToExclude: [] }; + } +} diff --git a/src/commands/log-files.ts b/src/commands/log-files.ts new file mode 100644 index 0000000..a469e7e --- /dev/null +++ b/src/commands/log-files.ts @@ -0,0 +1,90 @@ +import { glob } from 'glob'; +import * as fs from 'node:fs'; +import * as path from 'node:path'; +import { commands, ExtensionContext, Uri, ViewColumn, window, workspace } from 'vscode'; +import { Commands } from '../commands/commands'; +import * as log from '../log-services/logger'; + +export function register(context: ExtensionContext, storageUri: Uri): void { + const workspacePath = path.resolve(storageUri.fsPath + '/cql_ls_ws'); + + context.subscriptions.push( + commands.registerCommand(Commands.OPEN_SERVER_LOG, (column: ViewColumn) => + openServerLogFile(workspacePath, column), + ), + commands.registerCommand(Commands.OPEN_CLIENT_LOG, (column: ViewColumn) => + openClientLogFile(column), + ), + commands.registerCommand(Commands.OPEN_LOGS, () => openLogs()), + ); +} + +async function openClientLogFile(column: ViewColumn = ViewColumn.Active): Promise { + const logFileDetails = log.getLogFileDetails(); + if (!logFileDetails) { + const message = 'Could not open CQL extension log file. Log file path has not been configured.'; + log.warn(message); + window.showWarningMessage(message); + return false; + } + + let logFile = ''; + try { + const files = await glob(`${logFileDetails.baseName}.*`, { + cwd: logFileDetails.directory, + posix: true, + }); + + if (files && files.length > 0) { + files.sort(); + const newestFile = files[files.length - 1]; + logFile = path.resolve(logFileDetails.directory, newestFile.toString()); + } + } catch (error) { + log.error('Glob error while looking for client logs:', error); + } + + return await openLogFile(logFile, 'Could not open CQL extension log file', column); +} + +async function openLogs() { + await commands.executeCommand(Commands.OPEN_CLIENT_LOG, ViewColumn.One); + await commands.executeCommand(Commands.OPEN_SERVER_LOG, ViewColumn.Two); +} + +function openLogFile( + logFile: string, + openingFailureWarning: string, + column: ViewColumn = ViewColumn.Active, +): Thenable { + if (!fs.existsSync(logFile)) { + log.warn(openingFailureWarning); + return window.showWarningMessage(openingFailureWarning).then(() => false); + } + + return workspace + .openTextDocument(logFile) + .then( + doc => { + if (!doc) { + return false; + } + return window.showTextDocument(doc, column).then(editor => !!editor); + }, + () => false, + ) + .then(didOpen => { + if (!didOpen) { + window.showWarningMessage(openingFailureWarning); + } + return didOpen; + }); +} + +function openServerLogFile( + workspacePath: string, + column: ViewColumn = ViewColumn.Active, +): Thenable { + const serverLogFile = path.join(workspacePath, '.metadata', '.log'); + return openLogFile(serverLogFile, 'Could not open CQL Language Server log file', column); +} diff --git a/src/commands/view-elm.ts b/src/commands/view-elm.ts new file mode 100644 index 0000000..d5be8f2 --- /dev/null +++ b/src/commands/view-elm.ts @@ -0,0 +1,27 @@ +import { commands, ExtensionContext, Uri, window, workspace } from 'vscode'; +import { getElm } from '../cql-service/cqlService.getElm'; +import * as log from '../log-services/logger'; +import { Commands } from './commands'; + +export function register(context: ExtensionContext): void { + context.subscriptions.push( + commands.registerCommand(Commands.VIEW_ELM_COMMAND_XML, async (uri: Uri) => { + viewElm(uri, 'xml'); + }), + commands.registerCommand(Commands.VIEW_ELM_COMMAND_JSON, async (uri: Uri) => { + viewElm(uri, 'json'); + }), + ); +} + +export async function viewElm(cqlFileUri: Uri, elmType: 'xml' | 'json' = 'xml') { + try { + log.debug(`attempting to get ELM from [${cqlFileUri}] as ${elmType}`); + const elm: string = await getElm(cqlFileUri, elmType); + workspace + .openTextDocument({ language: elmType, content: elm }) + .then(t => window.showTextDocument(t)); + } catch (error) { + window.showErrorMessage(`Error while converting ${cqlFileUri.fsPath} to ELM. err: ${error}`); + } +} diff --git a/src/cql-language-server/cqlLanguageClient.ts b/src/cql-language-server/cqlLanguageClient.ts new file mode 100644 index 0000000..8ba0eaa --- /dev/null +++ b/src/cql-language-server/cqlLanguageClient.ts @@ -0,0 +1,198 @@ +import { commands, ExtensionContext, window } from 'vscode'; +import { + CancellationToken, + ConfigurationParams, + ConfigurationRequest, + ExecuteCommandParams, + ExecuteCommandRequest, + LanguageClientOptions, + MessageType, + State, + StateChangeEvent, +} from 'vscode-languageclient'; +import { LanguageClient } from 'vscode-languageclient/node'; +import { Commands } from '../commands/commands'; +import { ClientStatus } from '../extension.api'; +import { RequirementsData } from '../java-support/requirements'; +import * as log from '../log-services/logger'; +import { + ActionableNotification, + ExecuteClientCommandRequest, + ProgressReportNotification, +} from '../protocol'; +import { statusBar } from '../statusBar'; +import { prepareExecutable } from './languageServerStarter'; + +export class CqlLanguageClient { + private languageClient: LanguageClient | undefined; + private status: ClientStatus = ClientStatus.Uninitialized; + extensionName = 'Language Support for CQL'; + + public async initialize( + context: ExtensionContext, + requirements: RequirementsData, + clientOptions: LanguageClientOptions, + workspacePath: string, + ): Promise { + if (this.status !== ClientStatus.Uninitialized) { + return; + } + + const serverOptions = prepareExecutable(requirements, context, workspacePath); + // Create the language client and start the client. + this.languageClient = new LanguageClient( + 'cql', + this.extensionName, + serverOptions, + clientOptions, + ); + + context.subscriptions.push( + this.languageClient.onDidChangeState((e: StateChangeEvent) => { + log.info( + `CqlLanguageClient's internal LanguageClient state changed from ${State[e.oldState]} to ${State[e.newState]}`, + ); + }), + ); + + this.languageClient.onReady().then(() => { + //log.info('language client is ready'); + this.status = ClientStatus.Started; + commands.executeCommand('setContext', 'cql.languageServerReady', true); + const version = requirements.cql_ls_info.cql_ls_jar.match(/(\d+\.\d+\.\d+)/)?.[1]; + // this.languageClient.onNotification(StatusNotification.type, (report) => { + // switch (report.type) { + // case 'ServiceReady': + // break; + // case 'Started': + // this.status = ClientStatus.Started; + // statusBar.setReady(); + // break; + // case 'Error': + // this.status = ClientStatus.Error; + // statusBar.setError(); + // break; + // case 'Starting': + // case 'Message': + // // message goes to progress report instead + // break; + // } + // statusBar.updateTooltip(report.message); + // }); + + this.languageClient!.onNotification(ProgressReportNotification.type, _progress => { + // TODO: Support for long-running tasks + }); + + this.languageClient!.onNotification(ActionableNotification.type, notification => { + const titles = notification.commands!.map(a => a.title); + let show = null; + switch (notification.severity) { + case MessageType.Log: + logNotification(notification.message); + break; + case MessageType.Info: + show = window.showInformationMessage; + break; + case MessageType.Warning: + show = window.showWarningMessage; + break; + case MessageType.Error: + show = window.showErrorMessage; + break; + } + if (!show) { + return; + } + + show(notification.message, ...titles).then((selection: string | undefined) => { + for (const action of notification.commands!) { + if (action.title === selection) { + const args: any[] = action.arguments ? action.arguments : []; + commands.executeCommand(action.command, ...args); + break; + } + } + + return null; + }); + }); + + this.languageClient!.onRequest(ExecuteClientCommandRequest.type, params => { + return commands.executeCommand(params.command, ...params.arguments!); + }); + + this.languageClient!.onRequest(ConfigurationRequest.type, (_params: ConfigurationParams) => { + // TODO: This is a request for workspace configuration. In the context of the IG + // this ought to be the cql-options file at least. + + return null; + }); + + // TODO: Set this once we have the initialization signal from the LS. + statusBar.setReady(version); + }); + + //this.registerCommands(context); + this.status = ClientStatus.Initialized; + } + + private registerCommands(context: ExtensionContext): void { + context.subscriptions.push( + commands.registerCommand(Commands.OPEN_OUTPUT, () => + this.languageClient!.outputChannel.show(), + ), + ); + } + + public async start() { + if (this.languageClient && this.status === ClientStatus.Initialized) { + //log.debug('attempting to start language client'); + await this.languageClient.start(); + this.status = ClientStatus.Starting; + } + } + + public async stop() { + if (this.languageClient) { + //log.debug('attempting to stop language client'); + await this.languageClient.stop(); + this.status = ClientStatus.Stopping; + } + } + + public getClient(): LanguageClient { + return this.languageClient!; + } + + public getClientStatus(): ClientStatus { + return this.status; + } +} + +function logNotification(message: string) { + return new Promise(() => { + log.info(message); + }); +} + +export const cqlLanguageClientInstance: CqlLanguageClient = new CqlLanguageClient(); + +export async function sendRequest( + command: string, + args: any[], + token: CancellationToken | undefined = undefined, +): Promise { + const params: ExecuteCommandParams = { + command, + arguments: args, + }; + if (token) { + return await cqlLanguageClientInstance + .getClient() + .sendRequest(ExecuteCommandRequest.type, params, token); + } + return await cqlLanguageClientInstance + .getClient() + .sendRequest(ExecuteCommandRequest.type, params); +} diff --git a/src/languageServerStarter.ts b/src/cql-language-server/languageServerStarter.ts similarity index 88% rename from src/languageServerStarter.ts rename to src/cql-language-server/languageServerStarter.ts index 1172a8e..3578011 100644 --- a/src/languageServerStarter.ts +++ b/src/cql-language-server/languageServerStarter.ts @@ -6,8 +6,8 @@ import { StreamInfo, TransportKind, } from 'vscode-languageclient/node'; -import { logger } from './log'; -import { RequirementsData } from './requirements'; +import { RequirementsData } from '../java-support/requirements'; +import * as log from '../log-services/logger'; import { ExtensionContext } from 'vscode'; @@ -17,6 +17,33 @@ const DEBUG = typeof v8debug === 'object' || startedInDebugMode(); export interface TransportExecutable extends Executable { transport: TransportKind; } + +export function awaitServerConnection(port: string): Thenable { + const addr = parseInt(port); + return new Promise((res, rej) => { + const server = net.createServer(stream => { + server.close(); + log.info('CQL LS connection established on port ' + addr); + res({ reader: stream, writer: stream }); + }); + server.on('error', rej); + server.listen(addr, () => { + server.removeListener('error', rej); + log.info('Awaiting CQL LS connection on port ' + addr); + }); + return server; + }); +} + +// If vscode is started with a debug flag this enables the java ls debug as well. +export function hasDebugFlag(args: string[]): boolean { + if (args) { + // See https://nodejs.org/en/docs/guides/debugging-getting-started/ + return args.some(arg => /^--inspect/.test(arg) || /^--debug/.test(arg)); + } + return false; +} + export function prepareExecutable( requirements: RequirementsData, context: ExtensionContext, @@ -28,28 +55,12 @@ export function prepareExecutable( executable.options = options; executable.command = path.resolve(requirements.java_requirements.java_home + '/bin/java'); executable.args = prepareParams(requirements, context, workspacePath); - logger.info(`Starting CQL server with: ${executable.command} ${executable.args.join(' ')}`); + log.info(`Starting CQL server with: ${executable.command} ${executable.args.join(' ')}`); executable.transport = TransportKind.stdio; return executable; } -export function awaitServerConnection(port: string): Thenable { - const addr = parseInt(port); - return new Promise((res, rej) => { - const server = net.createServer(stream => { - server.close(); - logger.info('CQL LS connection established on port ' + addr); - res({ reader: stream, writer: stream }); - }); - server.on('error', rej); - server.listen(addr, () => { - server.removeListener('error', rej); - logger.info('Awaiting CQL LS connection on port ' + addr); - }); - return server; - }); -} function prepareParams( requirements: RequirementsData, @@ -79,12 +90,3 @@ function startedInDebugMode(): boolean { const args = (process as any).execArgv as string[]; return hasDebugFlag(args); } - -// If vscode is started with a debug flag this enables the java ls debug as well. -export function hasDebugFlag(args: string[]): boolean { - if (args) { - // See https://nodejs.org/en/docs/guides/debugging-getting-started/ - return args.some(arg => /^--inspect/.test(arg) || /^--debug/.test(arg)); - } - return false; -} diff --git a/src/cql-service/README.md b/src/cql-service/README.md new file mode 100644 index 0000000..0ddfff8 --- /dev/null +++ b/src/cql-service/README.md @@ -0,0 +1,13 @@ +# CQL Service Modules + +The cqlService modules are intended to be the link between the VSCode extension and the external services that handle CQL operations. + +Currently, CQL operations are passed through the CQL Language Server(java) via the cqlLanguageClient. +Future plans intent to move the CQL operations to a JS based library(s) which will remove the need for a CQL Language Server. + +_Note: There should be no UI operations in these modules, those are 'upstream' functions. Limit code in these modules to dealing with +interacting with external services/libraries only._ + +_Note: It is acceptable to use the cqlLanguageClient sendRequest function to handle the REST operations needed to interact with an +external Language Server. The communication between the extension and the Language Server is an implementation detail that +only clutters the `business logic` of cql operations._ diff --git a/src/cql-service/cqlService.executeCql.ts b/src/cql-service/cqlService.executeCql.ts new file mode 100644 index 0000000..feef416 --- /dev/null +++ b/src/cql-service/cqlService.executeCql.ts @@ -0,0 +1,89 @@ +import * as fs from 'fs'; +import path from 'node:path'; +import { Uri } from 'vscode'; +import { Commands } from '../commands/commands'; +import { sendRequest } from '../cql-language-server/cqlLanguageClient'; +import { TestCase } from '../model/testCase'; + +export async function executeCql( + cqlFileUri: Uri, + testCases: Array, + terminologyUri: Uri, + fhirVersion: string, + optionsPath: Uri, + rootDir: Uri, + contextValue?: string, + measurementPeriod?: string, +): Promise { + let testCasesArgs: string[] = []; + for (let testCase of testCases) { + testCasesArgs.push( + ...getExecArgs( + cqlFileUri, + testCase.path, + terminologyUri, + contextValue ?? testCase.name, + measurementPeriod, + ), + ); + } + + let operationArgs = getCqlCommandArgs(fhirVersion, optionsPath, rootDir); + operationArgs.push(...testCasesArgs); + return await sendRequest(Commands.EXECUTE_CQL, operationArgs); +} + +function getCqlCommandArgs(fhirVersion: string, optionsPath: Uri, rootDir: Uri): string[] { + const args = ['cql']; + + args.push(`-fv=${fhirVersion}`); + + if (optionsPath && fs.existsSync(optionsPath.fsPath)) { + args.push(`-op=${optionsPath}`); + } + + if (rootDir) { + args.push(`-rd=${rootDir}`); + } + + return args; +} + +function getExecArgs( + cqlFileUri: Uri, + testCaseUri?: Uri, + terminologyUri?: Uri, + contextValue?: string, + measurementPeriod?: string, +): string[] { + // TODO: One day we might support other models and contexts + const modelType = 'FHIR'; + const contextType = 'Patient'; + + let args: string[] = []; + args.push( + `-ln=${path.basename(cqlFileUri.fsPath, '.cql')}`, + `-lu=${Uri.file(path.dirname(cqlFileUri.fsPath))}`, + ); + + if (testCaseUri) { + args.push(`-m=${modelType}`, `-mu=${testCaseUri}`); + } + + if (terminologyUri) { + args.push(`-t=${terminologyUri}`); + } + + if (contextValue) { + args.push(`-c=${contextType}`, `-cv=${contextValue}`); + } + + if (measurementPeriod && measurementPeriod !== '') { + args.push( + `-p=${path.basename(cqlFileUri.fsPath)}."Measurement Period"`, + `-pv=${measurementPeriod}`, + ); + } + + return args; +} diff --git a/src/cql-service/cqlService.getElm.ts b/src/cql-service/cqlService.getElm.ts new file mode 100644 index 0000000..f3449d7 --- /dev/null +++ b/src/cql-service/cqlService.getElm.ts @@ -0,0 +1,7 @@ +import { Uri } from 'vscode'; +import { Commands } from '../commands/commands'; +import { sendRequest } from '../cql-language-server/cqlLanguageClient'; + +export async function getElm(cqlFileUri: Uri, elmType: 'xml' | 'json'): Promise { + return await sendRequest(Commands.VIEW_ELM, [cqlFileUri.toString(), elmType]); +} diff --git a/src/cqlLanguageClient.ts b/src/cqlLanguageClient.ts deleted file mode 100644 index e6da2f4..0000000 --- a/src/cqlLanguageClient.ts +++ /dev/null @@ -1,223 +0,0 @@ -import { commands, ExtensionContext, Uri, window, workspace } from "vscode"; -import { - ConfigurationParams, - ConfigurationRequest, - LanguageClientOptions, - MessageType, -} from "vscode-languageclient"; -import { LanguageClient } from "vscode-languageclient/node"; -import { Commands } from "./commands"; -import { executeCQLFile } from "./executeCql"; -import { ClientStatus } from "./extension.api"; -import { prepareExecutable } from "./languageServerStarter"; -import { logger } from "./log"; -import { - ActionableNotification, - ExecuteClientCommandRequest, - ProgressReportNotification, -} from "./protocol"; -import { RequirementsData } from "./requirements"; -import { statusBar } from "./statusBar"; - -const extensionName = "Language Support for CQL"; - -export class CqlLanguageClient { - private languageClient: LanguageClient | undefined; - private status: ClientStatus = ClientStatus.Uninitialized; - - public async initialize( - context: ExtensionContext, - requirements: RequirementsData, - clientOptions: LanguageClientOptions, - workspacePath: string, - ): Promise { - if (this.status !== ClientStatus.Uninitialized) { - return; - } - - const serverOptions = prepareExecutable( - requirements, - context, - workspacePath, - ); - // Create the language client and start the client. - this.languageClient = new LanguageClient( - "cql", - extensionName, - serverOptions, - clientOptions, - ); - - this.languageClient.onReady().then(() => { - this.status = ClientStatus.Started; - // this.languageClient.onNotification(StatusNotification.type, (report) => { - // switch (report.type) { - // case 'ServiceReady': - // break; - // case 'Started': - // this.status = ClientStatus.Started; - // statusBar.setReady(); - // break; - // case 'Error': - // this.status = ClientStatus.Error; - // statusBar.setError(); - // break; - // case 'Starting': - // case 'Message': - // // message goes to progress report instead - // break; - // } - // statusBar.updateTooltip(report.message); - // }); - - this.languageClient!.onNotification( - ProgressReportNotification.type, - (_progress) => { - // TODO: Support for long-running tasks - }, - ); - - this.languageClient!.onNotification( - ActionableNotification.type, - (notification) => { - const titles = notification.commands!.map((a) => a.title); - let show = null; - switch (notification.severity) { - case MessageType.Log: - logNotification(notification.message); - break; - case MessageType.Info: - show = window.showInformationMessage; - break; - case MessageType.Warning: - show = window.showWarningMessage; - break; - case MessageType.Error: - show = window.showErrorMessage; - break; - } - if (!show) { - return; - } - - show(notification.message, ...titles).then( - (selection: string | undefined) => { - for (const action of notification.commands!) { - if (action.title === selection) { - const args: any[] = action.arguments ? action.arguments : []; - commands.executeCommand(action.command, ...args); - break; - } - } - - return null; - }, - ); - }, - ); - - this.languageClient!.onRequest( - ExecuteClientCommandRequest.type, - (params) => { - return commands.executeCommand(params.command, ...params.arguments!); - }, - ); - - this.languageClient!.onRequest( - ConfigurationRequest.type, - (_params: ConfigurationParams) => { - // TODO: This is a request for workspace configuration. In the context of the IG - // this ought to be the cql-options file at least. - - return null; - }, - ); - - // TODO: Set this once we have the initialization signal from the LS. - statusBar.setReady(); - }); - - this.registerCommands(context); - this.status = ClientStatus.Initialized; - } - - private registerCommands(context: ExtensionContext): void { - context.subscriptions.push( - commands.registerCommand(Commands.OPEN_OUTPUT, () => - this.languageClient!.outputChannel.show(), - ), - ); - - context.subscriptions.push( - commands.registerCommand( - Commands.VIEW_ELM_COMMAND_XML, - async (uri: Uri) => { - const elmAsXml: string = await (( - commands.executeCommand( - Commands.EXECUTE_WORKSPACE_COMMAND, - Commands.VIEW_ELM, - uri.toString(), - "xml", - ) - )); - workspace - .openTextDocument({ language: "xml", content: elmAsXml }) - .then((t) => window.showTextDocument(t)); - }, - ), - commands.registerCommand( - Commands.VIEW_ELM_COMMAND_JSON, - async (uri: Uri) => { - const elmAsJson: string = await (( - commands.executeCommand( - Commands.EXECUTE_WORKSPACE_COMMAND, - Commands.VIEW_ELM, - uri.toString(), - "json", - ) - )); - workspace - .openTextDocument({ language: "json", content: elmAsJson }) - .then((t) => window.showTextDocument(t)); - }, - ), - ); - - context.subscriptions.push( - commands.registerCommand( - Commands.EXECUTE_CQL_COMMAND, - async (uri: Uri) => { - await executeCQLFile(uri); - }, - ), - ); - } - - public start(): void { - if (this.languageClient && this.status === ClientStatus.Initialized) { - this.languageClient.start(); - this.status = ClientStatus.Starting; - } - } - - public stop() { - if (this.languageClient) { - this.languageClient.stop(); - this.status = ClientStatus.Stopping; - } - } - - public getClient(): LanguageClient { - return this.languageClient!; - } - - public getClientStatus(): ClientStatus { - return this.status; - } -} - -function logNotification(message: string) { - return new Promise(() => { - logger.verbose(message); - }); -} diff --git a/src/executeCql.ts b/src/executeCql.ts deleted file mode 100644 index eaf30d6..0000000 --- a/src/executeCql.ts +++ /dev/null @@ -1,258 +0,0 @@ -import * as fs from 'fs'; -import * as fse from 'fs-extra'; -import { glob } from 'glob'; -import path from 'path'; -import { Position, TextEditor, Uri, commands, window, workspace } from 'vscode'; -import { Utils } from 'vscode-uri'; -import { Commands } from './commands'; - -interface TestCase { - name: string | null; - path: Uri | null; -} - -interface TestCaseExclusion { - library: string; - testCase: string; - reason: string; -} - -interface TestConfig { - testCasesToExclude: TestCaseExclusion[]; -} - -async function executeCQL(textEditor: TextEditor, operationArgs: string[]) { - const startExecution = Date.now(); - const result: string | undefined = await commands.executeCommand( - Commands.EXECUTE_WORKSPACE_COMMAND, - Commands.EXECUTE_CQL, - ...operationArgs, - ); - const endExecution = Date.now(); - - await insertLineAtEnd(textEditor, result!); - await insertLineAtEnd( - textEditor, - `elapsed: ${((endExecution - startExecution) / 1000).toString()} seconds`, - ); -} - -// NOTE: This is not the intended future state of executing CQL. -// There's a CQL debug server under development that will replace this. -export async function executeCQLFile(uri: Uri): Promise { - if (!fs.existsSync(uri.fsPath)) { - window.showInformationMessage('No library content found. Please save before executing.'); - return; - } - - const libraryDirectory = Utils.dirname(uri); - const libraryName = Utils.basename(uri).replace('.cql', '').split('-')[0]; - const projectPath = workspace.getWorkspaceFolder(uri)!.uri; - - // todo: make this a setting - let terminologyPath: Uri = Utils.resolvePath(projectPath, 'input', 'vocabulary', 'valueset'); - - let fhirVersion = getFhirVersion(); - if (!fhirVersion) { - fhirVersion = 'R4'; - window.showInformationMessage('Unable to determine version of FHIR used. Defaulting to R4.'); - } - - const rootDir = Utils.resolvePath(projectPath); - const optionsPath = Utils.resolvePath(libraryDirectory, 'cql-options.json'); - const testPath = Utils.resolvePath(projectPath, 'input', 'tests'); - const resultPath = Utils.resolvePath(testPath, 'results'); - - const outputPath = Utils.resolvePath(resultPath, `${libraryName}.txt`); - fse.ensureFileSync(outputPath.fsPath); - const textDocument = await workspace.openTextDocument(outputPath); - const textEditor = await window.showTextDocument(textDocument); - - const testConfigPath = Utils.resolvePath(testPath, 'config.json'); - const testConfig = loadTestConfig(testConfigPath); - const excludedTestCases = getExcludedTestCases(libraryName, testConfig.testCasesToExclude); - - let testCasesArgs: string[] = []; - let testPaths = getTestPaths(testPath, libraryName, Array.from(excludedTestCases.keys())); - - // We didn't find any test cases, so we'll just execute an empty one - if (testPaths.length === 0) { - testPaths.push({ name: null, path: null }); - } - - const measurementPeriod = ''; - for (let p of testPaths) { - testCasesArgs.push( - ...getExecArgs( - libraryDirectory, - libraryName, - p.path, - terminologyPath, - p.name, - measurementPeriod, - ), - ); - } - - const cqlMessage = `CQL: ${libraryDirectory.fsPath}`; - const terminologyMessage = fs.existsSync(terminologyPath.fsPath) - ? `Terminology: ${terminologyPath.fsPath}` - : `No terminology found at ${terminologyPath.fsPath}. Evaluation may fail if terminology is required.`; - - let testMessage = []; - if (testPaths.length == 1 && testPaths[0].name === null) { - testMessage.push( - `No data found at ${testPath.fsPath}. Evaluation may fail if data is required.`, - ); - } else { - testMessage.push(`Test cases:`); - for (let p of testPaths) { - testMessage.push(`${p.name} - ${p.path?.fsPath}`); - } - } - - if (excludedTestCases.size > 0) { - testMessage.push('\nExcluded test cases:'); - for (const [testCase, reason] of excludedTestCases.entries()) { - testMessage.push(`${testCase} - ${reason}`); - } - } - - await insertLineAtEnd(textEditor, `${cqlMessage}`); - await insertLineAtEnd(textEditor, `${terminologyMessage}`); - await insertLineAtEnd(textEditor, `${testMessage.join('\n')}\n`); - - let operationArgs = getCqlCommandArgs(fhirVersion, optionsPath, rootDir); - operationArgs.push(...testCasesArgs); - await executeCQL(textEditor, operationArgs); -} - -function getCqlCommandArgs(fhirVersion: string, optionsPath: Uri, rootDir: Uri): string[] { - const args = ['cql']; - - args.push(`-fv=${fhirVersion}`); - - if (optionsPath && fs.existsSync(optionsPath.fsPath)) { - args.push(`-op=${optionsPath}`); - } - - if (rootDir) { - args.push(`-rd=${rootDir}`); - } - - return args; -} - -function getExcludedTestCases( - libraryName: string, - testCasesToExclude: TestCaseExclusion[], -): Map { - let excludedTestCases = new Map(); - for (let excludedTestCase of testCasesToExclude) { - if (excludedTestCase.library == libraryName) { - excludedTestCases.set(excludedTestCase.testCase, excludedTestCase.reason); - } - } - return excludedTestCases; -} - -function getExecArgs( - libraryDirectory: Uri, - libraryName: string, - modelPath: Uri | null, - terminologyPath: Uri | null, - contextValue: string | null, - measurementPeriod: string, -): string[] { - // TODO: One day we might support other models and contexts - const modelType = 'FHIR'; - const contextType = 'Patient'; - - let args: string[] = []; - args.push(`-ln=${libraryName}`, `-lu=${libraryDirectory}`); - - if (modelPath) { - args.push(`-m=${modelType}`, `-mu=${modelPath}`); - } - - if (terminologyPath) { - args.push(`-t=${terminologyPath}`); - } - - if (contextValue) { - args.push(`-c=${contextType}`, `-cv=${contextValue}`); - } - - if (measurementPeriod && measurementPeriod !== '') { - args.push(`-p=${libraryName}."Measurement Period"`, `-pv=${measurementPeriod}`); - } - - return args; -} - -function getFhirVersion(): string | null { - const fhirVersionRegex = /using (FHIR|"FHIR") version '(\d(.|\d)*)'/; - const matches = window.activeTextEditor!.document.getText().match(fhirVersionRegex); - if (matches && matches.length > 2) { - const version = matches[2]; - if (version.startsWith('2')) { - return 'DSTU2'; - } else if (version.startsWith('3')) { - return 'DSTU3'; - } else if (version.startsWith('4')) { - return 'R4'; - } else if (version.startsWith('5')) { - return 'R5'; - } - } - - return null; -} - -/** - * Get the test cases to execute - * @param testPath the root path to look for test cases - * @returns a list of test cases to execute - */ -function getTestPaths( - testPath: Uri, - libraryName: string, - testCasesToExclude: string[], -): TestCase[] { - if (!fs.existsSync(testPath.fsPath)) { - return []; - } - - let testCases: TestCase[] = []; - let directories = glob - .sync(testPath.fsPath + `/**/${libraryName}`) - .filter(d => fs.statSync(d).isDirectory()); - for (let dir of directories) { - let cases = fs - .readdirSync(dir) - .filter(d => fs.statSync(path.join(dir, d)).isDirectory() && !testCasesToExclude.includes(d)); - for (let c of cases) { - testCases.push({ name: c, path: Uri.file(path.join(dir, c)) }); - } - } - - return testCases; -} - -async function insertLineAtEnd(textEditor: TextEditor, text: string) { - const document = textEditor.document; - await textEditor.edit(editBuilder => { - editBuilder.insert(new Position(document.lineCount, 0), text + '\n'); - }); -} - -function loadTestConfig(testConfigPath: Uri): TestConfig { - try { - const jsonString = fs.readFileSync(testConfigPath.fsPath, 'utf-8'); - // Cast the parsed object to the User interface - return JSON.parse(jsonString) as TestConfig; - } catch (error) { - console.error('Error reading or parsing JSON file:', error); - return { testCasesToExclude: [] }; - } -} diff --git a/src/extension.ts b/src/extension.ts index 09f8211..6754e64 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,29 +1,24 @@ -import * as fs from 'fs'; -import { glob } from 'glob'; -import * as os from 'os'; import * as path from 'path'; -import { ExtensionContext, OutputChannel, ViewColumn, commands, window, workspace } from 'vscode'; +import { commands, ExtensionContext, Uri, window } from 'vscode'; import { - CancellationToken, CloseAction, ErrorAction, ErrorHandler, - ExecuteCommandParams, - ExecuteCommandRequest, LanguageClientOptions, Message, RevealOutputChannelOn, } from 'vscode-languageclient'; -import { Commands } from './commands'; -import { CqlLanguageClient } from './cqlLanguageClient'; +import { Commands } from './commands/commands'; +import { register as registerExecuteCql } from './commands/execute-cql'; +import { register as registerLogCommands } from './commands/log-files'; +import { register as registerViewElmCommand } from './commands/view-elm'; +import { cqlLanguageClientInstance } from './cql-language-server/cqlLanguageClient'; import { ClientStatus } from './extension.api'; -import { initializeLogFile, logger } from './log'; -import * as requirements from './requirements'; +import * as requirements from './java-support/requirements'; +import * as log from './log-services/logger'; import { statusBar } from './statusBar'; -const cqlLanguageClient: CqlLanguageClient = new CqlLanguageClient(); -const extensionName = 'Language Support for CQL'; -let clientLogFile: string; +export const EXTENSION_NAME = 'Language Support for CQL'; export class ClientErrorHandler implements ErrorHandler { private restarts: number[]; @@ -34,30 +29,24 @@ export class ClientErrorHandler implements ErrorHandler { public error(_error: Error, _message: Message, count: number): ErrorAction { if (count && count <= 3) { - logger.error( - `${this.name} server encountered error: ${_message}, ${_error && _error.toString()}`, - ); + log.error(`${this.name} server encountered error: ${_message}`, _error); return ErrorAction.Continue; } - logger.error( - `${this.name} server encountered error and will shut down: ${_message}, ${ - _error && _error.toString() - }`, - ); + log.error(`${this.name} server encountered error and will shut down: ${_message}`, _error); return ErrorAction.Shutdown; } public closed(): CloseAction { this.restarts.push(Date.now()); if (this.restarts.length < 5) { - logger.error(`The ${this.name} server crashed and will restart.`); + log.error(`The ${this.name} server crashed and will restart.`); return CloseAction.Restart; } else { const diff = this.restarts[this.restarts.length - 1] - this.restarts[0]; if (diff <= 3 * 60 * 1000) { const message = `The ${this.name} server crashed 5 times in the last 3 minutes. The server will not be restarted.`; - logger.error(message); + log.error(message); const action = 'Show logs'; window.showErrorMessage(message, action).then(selection => { if (selection === action) { @@ -67,65 +56,32 @@ export class ClientErrorHandler implements ErrorHandler { return CloseAction.DoNotRestart; } - logger.error(`The ${this.name} server crashed and will restart.`); + log.error(`The ${this.name} server crashed and will restart.`); this.restarts.shift(); return CloseAction.Restart; } } } -export class OutputInfoCollector implements OutputChannel { - private channel: OutputChannel; - - constructor(public name: string) { - this.channel = window.createOutputChannel(this.name); - } - replace(value: string): void { - this.clear(); - this.append(value); - } - - append(value: string): void { - logger.info(value); - this.channel.append(value); - } - - appendLine(value: string): void { - logger.info(value); - this.channel.appendLine(value); - } - - clear(): void { - this.channel.clear(); - } - - show(preserveFocus?: boolean): void; - show(column?: ViewColumn, preserveFocus?: boolean): void; - show(_column?: any, preserveFocus?: any) { - this.channel.show(preserveFocus); - } - - hide(): void { - this.channel.hide(); - } - - dispose(): void { - this.channel.dispose(); +function getStorageUri(context: ExtensionContext): Uri { + const workspaceStorageUri = context.storageUri; + if (workspaceStorageUri) { + return workspaceStorageUri; + } else { + throw new Error( + 'No workspace storage URI available (e.g., when running an untitled workspace)', + ); } } export function activate(context: ExtensionContext): Promise { - let storagePath = context.storagePath; - if (!storagePath) { - storagePath = getTempWorkspace(); - } - clientLogFile = path.join(storagePath, 'client.log'); - initializeLogFile(clientLogFile); + const storageUri = getStorageUri(context); + const outputChannel = log.initialize(Uri.joinPath(storageUri, 'logs'), EXTENSION_NAME); return requirements .resolveRequirements(context) .catch(error => { - // show error + //log.error(error.message); window.showErrorMessage(error.message, error.label).then(selection => { if (error.label && error.label === selection && error.command) { commands.executeCommand(error.command, error.commandParam); @@ -135,69 +91,34 @@ export function activate(context: ExtensionContext): Promise { throw error; }) .then(async requirements => { - return new Promise(async resolve => { - const workspacePath = path.resolve(storagePath + '/cql_ls_ws'); - // Options to control the language client - const clientOptions: LanguageClientOptions = { - // Register the server for CQL - documentSelector: [ - { scheme: 'file', language: 'cql' }, - { scheme: 'untitled', language: 'cql' }, - ], - revealOutputChannelOn: RevealOutputChannelOn.Warn, // TODO: The Debug output should be handled a different way. - errorHandler: new ClientErrorHandler(extensionName), - initializationFailedHandler: error => { - logger.error( - `Failed to initialize ${extensionName} due to ${error && error.toString()}`, - ); - return true; - }, - outputChannel: new OutputInfoCollector(extensionName), - outputChannelName: extensionName, - }; - - context.subscriptions.push( - commands.registerCommand(Commands.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => { - let token: CancellationToken | undefined; - let commandArgs: any[] = rest; - if (rest && rest.length && CancellationToken.is(rest[rest.length - 1])) { - token = rest[rest.length - 1]; - commandArgs = rest.slice(0, rest.length - 1); - } - const params: ExecuteCommandParams = { - command, - arguments: commandArgs, - }; - if (token) { - return cqlLanguageClient - .getClient() - .sendRequest(ExecuteCommandRequest.type, params, token); - } else { - return cqlLanguageClient.getClient().sendRequest(ExecuteCommandRequest.type, params); - } - }), - ); - - // Register commands here to make it available even when the language client fails - context.subscriptions.push( - commands.registerCommand(Commands.OPEN_SERVER_LOG, (column: ViewColumn) => - openServerLogFile(workspacePath, column), - ), - ); - - context.subscriptions.push( - commands.registerCommand(Commands.OPEN_CLIENT_LOG, (column: ViewColumn) => - openClientLogFile(clientLogFile, column), - ), - ); - - context.subscriptions.push(commands.registerCommand(Commands.OPEN_LOGS, () => openLogs())); - - context.subscriptions.push(statusBar); - - await startServer(context, requirements, clientOptions, workspacePath); - resolve(); - }); + //log.info('extension activated'); + const workspacePath = path.resolve(storageUri.fsPath + '/cql_ls_ws'); + + // Options to control the language client + const clientOptions: LanguageClientOptions = { + // Register the server for CQL + documentSelector: [ + { scheme: 'file', language: 'cql' }, + { scheme: 'untitled', language: 'cql' }, + ], + revealOutputChannelOn: RevealOutputChannelOn.Warn, // TODO: The Debug output should be handled a different way. + errorHandler: new ClientErrorHandler(EXTENSION_NAME), + initializationFailedHandler: error => { + const message = `Failed to initialize ${EXTENSION_NAME} due to ${error && error.toString()}`; + //log.error(message); + window.showErrorMessage(message, error.label); + return true; + }, + outputChannel: outputChannel, + outputChannelName: EXTENSION_NAME, + }; + + registerExecuteCql(context); + registerLogCommands(context, storageUri); + registerViewElmCommand(context); + context.subscriptions.push(statusBar); + + await startServer(context, requirements, clientOptions, workspacePath); }); } @@ -206,114 +127,24 @@ async function startServer( requirements: requirements.RequirementsData, clientOptions: LanguageClientOptions, workspacePath: string, -) { - if (cqlLanguageClient.getClientStatus() !== ClientStatus.Uninitialized) { +): Promise { + if (cqlLanguageClientInstance.getClientStatus() !== ClientStatus.Uninitialized) { return; } - await cqlLanguageClient.initialize(context, requirements, clientOptions, workspacePath); - cqlLanguageClient.start(); - statusBar.showStatusBar(); -} - -export function deactivate(): void { - cqlLanguageClient.stop(); -} - -function openServerLogFile( - workspacePath: string, - column: ViewColumn = ViewColumn.Active, -): Thenable { - const serverLogFile = path.join(workspacePath, '.metadata', '.log'); - return openLogFile(serverLogFile, 'Could not open CQL Language Server log file', column); -} - -async function openClientLogFile( - logFile: string, - column: ViewColumn = ViewColumn.Active, -): Promise { - const filename = path.basename(logFile); - const dirname = path.dirname(logFile); - try { - const files = await glob(`${filename}.*`, { - cwd: dirname, - posix: true - }); - - if (files && files.length > 0) { - files.sort(); - const newestFile = files[files.length - 1]; - logFile = path.resolve(dirname, newestFile.toString()); - } - } catch (err) { - logger.error('Glob error while looking for client logs:', err); + await cqlLanguageClientInstance.initialize(context, requirements, clientOptions, workspacePath); + await cqlLanguageClientInstance.start(); + statusBar.showStatusBar(); + } catch (error) { + log.error(`Failed to start server: ${error}`); } - - return await openLogFile(logFile, 'Could not open CQL extension log file', column); -} - -async function openLogs() { - await commands.executeCommand(Commands.OPEN_CLIENT_LOG, ViewColumn.One); - await commands.executeCommand(Commands.OPEN_SERVER_LOG, ViewColumn.Two); -} - -function openLogFile( - logFile: string, - openingFailureWarning: string, - column: ViewColumn = ViewColumn.Active, -): Thenable { - if (!fs.existsSync(logFile)) { - return window.showWarningMessage('No log file available').then(() => false); - } - - return workspace - .openTextDocument(logFile) - .then( - doc => { - if (!doc) { - return false; - } - return window.showTextDocument(doc, column).then(editor => !!editor); - }, - () => false, - ) - .then(didOpen => { - if (!didOpen) { - window.showWarningMessage(openingFailureWarning); - } - return didOpen; - }); -} - -function getTempWorkspace() { - return path.resolve(os.tmpdir(), 'vscodesws_' + makeRandomHexString(5)); } -function makeRandomHexString(length: number) { - const chars = [ - '0', - '1', - '2', - '3', - '4', - '5', - '6', - '6', - '7', - '8', - '9', - 'a', - 'b', - 'c', - 'd', - 'e', - 'f', - ]; - let result = ''; - for (let i = 0; i < length; i++) { - const idx = Math.floor(chars.length * Math.random()); - result += chars[idx]; +export async function deactivate(): Promise { + log.info('extension deactivated'); + if (!cqlLanguageClientInstance) { + return undefined; } - return result; + return cqlLanguageClientInstance.stop(); } diff --git a/src/findJavaRuntimes.ts b/src/java-support/findJavaRuntimes.ts similarity index 100% rename from src/findJavaRuntimes.ts rename to src/java-support/findJavaRuntimes.ts diff --git a/src/javaServiceInstaller.ts b/src/java-support/javaServiceInstaller.ts similarity index 96% rename from src/javaServiceInstaller.ts rename to src/java-support/javaServiceInstaller.ts index e9f9f33..ba435ac 100644 --- a/src/javaServiceInstaller.ts +++ b/src/java-support/javaServiceInstaller.ts @@ -2,7 +2,7 @@ import * as fs from 'fs'; import fetch from 'node-fetch'; import * as path from 'path'; import { ExtensionContext, Progress, ProgressLocation, window } from 'vscode'; -import { ensureExists } from './helpers/file-helpers'; +import { ensureExists } from '../utils/file-utils'; interface MavenCoords { groupId: string; @@ -63,9 +63,7 @@ function getLocalName(coords: MavenCoords): string { function getSearchUrl(coords: MavenCoords): string { const groupIdAsDirectory = coords.groupId.replace(/\./gi, '/'); - return ( - `https://repo1.maven.org/maven2/${groupIdAsDirectory}/${coords.artifactId}/${coords.version}/${getLocalName(coords)}` - ); + return `https://repo1.maven.org/maven2/${groupIdAsDirectory}/${coords.artifactId}/${coords.version}/${getLocalName(coords)}`; } async function installServiceIfMissing(serviceName: string, coords: MavenCoords): Promise { diff --git a/src/requirements.ts b/src/java-support/requirements.ts similarity index 98% rename from src/requirements.ts rename to src/java-support/requirements.ts index d737331..8f9b137 100644 --- a/src/requirements.ts +++ b/src/java-support/requirements.ts @@ -2,7 +2,7 @@ import expandTilde from 'expand-tilde'; import * as fse from 'fs-extra'; import * as path from 'path'; import { ExtensionContext, Uri, env } from 'vscode'; -import { Commands } from './commands'; +import { Commands } from '../commands/commands'; import { JavaRuntime, findJavaHomes, getJavaVersion } from './findJavaRuntimes'; import { getServicePath, installJavaDependencies } from './javaServiceInstaller'; diff --git a/src/log-services/logger.ts b/src/log-services/logger.ts new file mode 100644 index 0000000..ce70cf7 --- /dev/null +++ b/src/log-services/logger.ts @@ -0,0 +1,48 @@ +import { LogOutputChannel, Uri } from 'vscode'; +import { MultiTransportLogger } from './multi-transport-logger'; + +let _logger: MultiTransportLogger; +let _logFilePath: Uri | undefined = undefined; + +/** + * Initializes Winston logger that integrates with VS Code's native logging. + * @param extensionPath The base path for log file storage. + * @param channelName The name visible in the VS Code Output dropdown. + */ +export function initialize(logFilePath: Uri, name: string): LogOutputChannel { + if (!_logger) { + _logFilePath = logFilePath; + _logger = new MultiTransportLogger(name, logFilePath); + } + return _logger; +} + +export function getLogFileDetails(): { directory: string; baseName: string } { + return _logger.getLogFileDetails(); +} + +export function debug(message: string, ...args: any[]): void { + _logger?.debug(message, args); +} + +export function error( + message: string, + error: Error | unknown | undefined = undefined, + ...args: any[] +) { + if (!error) { + _logger?.error(message, args); + } else if (error instanceof Error) { + _logger?.error(`${message} err: ${error.message}`, args); + } else { + _logger?.error(`${message} err: An unknown error occurred`, args); + } +} + +export function info(message: string, ...args: any[]): void { + _logger?.info(message, args); +} + +export function warn(message: string, ...args: any[]): void { + _logger?.warn(message, args); +} diff --git a/src/log-services/multi-transport-logger.ts b/src/log-services/multi-transport-logger.ts new file mode 100644 index 0000000..8f4e011 --- /dev/null +++ b/src/log-services/multi-transport-logger.ts @@ -0,0 +1,115 @@ +import { Event, LogLevel, LogOutputChannel, Uri, window } from 'vscode'; +import { createLogger, format, Logger as WinstonLogger } from 'winston'; +import DailyRotateFile from 'winston-daily-rotate-file'; +import { LogOutputChannelTransport } from 'winston-transport-vscode'; + +export class MultiTransportLogger implements LogOutputChannel { + private readonly _outputChannel: LogOutputChannel; + private readonly _dailyFileTransport: DailyRotateFile; + private readonly _logger: WinstonLogger; + private readonly _logFileBaseName: string; + private readonly _logFileStorageUri: Uri; + + readonly logLevel: LogLevel; + readonly onDidChangeLogLevel: Event; + readonly name: string; + + constructor(name: string, logFileStorageUri: Uri) { + this._outputChannel = window.createOutputChannel(name, { + log: true, + }); + this.logLevel = this._outputChannel.logLevel; + this.name = name.toLowerCase().replace(' ', '-'); + this.onDidChangeLogLevel = this._outputChannel.onDidChangeLogLevel; + + this._logFileStorageUri = logFileStorageUri; + this._logFileBaseName = this.name.toLowerCase().replace(/ /g, '-'); + + this._dailyFileTransport = new DailyRotateFile({ + filename: `${this._logFileBaseName}.log.%DATE%`, + datePattern: 'YYYY-MM-DD', + maxSize: '100k', // 100k max size per file + maxFiles: '7d', // retain logs of the last two days, + dirname: this._logFileStorageUri.fsPath, + extension: '.log', + }); + + this._logger = createLogger({ + level: 'debug', // Default level + format: format.combine( + format.timestamp({ format: 'YYYY-MM-DD HH:mm:ss' }), + format.errors({ stack: true }), + format.splat(), + format.json(), + ), + transports: [ + new LogOutputChannelTransport({ + outputChannel: this._outputChannel, + }), + this._dailyFileTransport, + ], + }); + } + + getOuptutChannelName(): string { + return this._outputChannel.name; + } + + getLogFileDetails(): { directory: string; baseName: string } { + return { directory: this._logFileStorageUri.fsPath, baseName: this._logFileBaseName }; + } + + trace(message: string, ...args: any[]): void { + this._logger.debug(message, args); + } + + debug(message: string, ...args: any[]): void { + this._logger.debug(message, args); + } + + info(message: string, ...args: any[]): void { + this._logger.info(message, args); + } + + warn(message: string, ...args: any[]): void { + this._logger.warn(message, args); + } + + error(error: string | Error, ...args: any[]): void { + this._logger.error(error); + } + + append(value: string): void { + this.appendLine(value); + } + + appendLine(value: string): void { + const lines = value.split('\n'); + + lines.forEach(line => { + if (line) this._logger.info(line); + }); + } + + replace(value: string): void { + this._outputChannel.replace(value); + } + + clear(): void { + this._outputChannel.clear(); + } + + show(preserveFocus?: unknown): void { + this._outputChannel.show(preserveFocus as boolean); + } + + hide(): void { + this._outputChannel.hide(); + } + + dispose(): void { + // a Winston Logger doesn't have a dispose function, this is needed for a plain outputchannel + // let the Winston Logger manage the outputchannel lifecycle + return; + } +} diff --git a/src/log.ts b/src/log.ts deleted file mode 100644 index c647996..0000000 --- a/src/log.ts +++ /dev/null @@ -1,27 +0,0 @@ -import { createLogger, format } from 'winston'; -import DailyRotateFile from 'winston-daily-rotate-file'; - -export function initializeLogFile(filename: string) { - logger.add( - new DailyRotateFile({ - filename: filename, - datePattern: 'YYYY-MM-DD', - maxSize: '100k', // 100k max size per file - maxFiles: '2d', // retain logs of the last two days - }), - ); -} - -export const logger = createLogger({ - format: format.combine( - format.timestamp({ - format: 'YYYY-MM-DD HH:mm:ss.SSS', - }), - format.prettyPrint(), - ), - transports: [ - // See https://github.com/microsoft/vscode/issues/117327 - // Disable console.log for lsp trace because intense logging will freeze the code render process. - // new transports.Console() - ], -}); diff --git a/src/model/testCase.ts b/src/model/testCase.ts new file mode 100644 index 0000000..f2684fb --- /dev/null +++ b/src/model/testCase.ts @@ -0,0 +1,85 @@ +import { glob } from 'glob'; +import * as fs from 'node:fs'; +import path from 'node:path'; +import { Uri } from 'vscode'; +import * as log from '../log-services/logger'; + +export interface TestCase { + name?: string; + path?: Uri; + description?: string; +} + +export interface TestCaseExclusion { + library: string; + testCase: string; + reason: string; +} + +/** + * Get the test cases to execute + * @param testPath the root path to look for test cases + * @returns a list of test cases to execute + */ +export function getTestCases( + testPath: Uri, + libraryName: string, + testCasesToExclude: string[], +): Array { + if (!fs.existsSync(testPath.fsPath)) { + return []; + } + + let testCases: TestCase[] = []; + let directories = glob + .sync(testPath.fsPath + `/**/${libraryName}`) + .filter(d => fs.statSync(d).isDirectory()); + for (let dir of directories) { + let cases = fs + .readdirSync(dir) + .filter(d => fs.statSync(path.join(dir, d)).isDirectory() && !testCasesToExclude.includes(d)); + for (let c of cases) { + testCases.push({ + name: c, + path: Uri.file(path.join(dir, c)), + description: getMeasureReportDescription(Uri.file(path.join(dir, c))), + }); + } + } + + return testCases; +} + +const TEST_CASE_DESCRIPTION_URL = + 'http://hl7.org/fhir/us/cqfmeasures/StructureDefinition/cqfm-testCaseDescription'; + +export function getMeasureReportDescription(testCaseFolderPath: Uri): string | undefined { + try { + if (!fs.existsSync(testCaseFolderPath.fsPath)) { + return undefined; + } + + const measureReportFile = fs + .readdirSync(testCaseFolderPath.fsPath) + .find(f => f.startsWith('MeasureReport')); + + if (!measureReportFile) { + return undefined; + } + + const content = fs.readFileSync( + path.join(testCaseFolderPath.fsPath, measureReportFile), + 'utf-8', + ); + const report = JSON.parse(content); + + const extension = ( + report.extension as { url: string; valueMarkdown?: string }[] | undefined + )?.find(e => e.url === TEST_CASE_DESCRIPTION_URL); + + return extension?.valueMarkdown; + } catch (error) { + log.error('Error while attempting to getMeasureReport Description', error); + } + return undefined; +} diff --git a/src/statusBar.ts b/src/statusBar.ts index 200363c..b3aeefb 100644 --- a/src/statusBar.ts +++ b/src/statusBar.ts @@ -1,4 +1,4 @@ -import { StatusBarItem, window, StatusBarAlignment } from 'vscode'; +import { MarkdownString, StatusBarAlignment, StatusBarItem, window } from 'vscode'; import { Disposable } from 'vscode-languageclient'; class StatusBar implements Disposable { @@ -10,7 +10,7 @@ class StatusBar implements Disposable { public showStatusBar(): void { this.statusBarItem.text = StatusIcon.Busy; - this.statusBarItem.tooltip = ''; + this.statusBarItem.tooltip = StatusTooltip.Busy; this.statusBarItem.show(); } @@ -20,14 +20,21 @@ class StatusBar implements Disposable { public setBusy(): void { this.statusBarItem.text = StatusIcon.Busy; + this.statusBarItem.tooltip = StatusTooltip.Busy; } public setError(): void { this.statusBarItem.text = StatusIcon.Error; + this.statusBarItem.tooltip = StatusTooltip.Error; } - public setReady(): void { + public setReady(version?: string): void { this.statusBarItem.text = StatusIcon.Ready; + const tooltip = new MarkdownString(StatusTooltip.Ready); + if (version) { + tooltip.appendMarkdown(`\n\nVersion: ${version}`); + } + this.statusBarItem.tooltip = tooltip; } public updateTooltip(tooltip: string): void { @@ -45,4 +52,10 @@ enum StatusIcon { Error = '$(error) CQL', } +enum StatusTooltip { + Busy = 'Server Busy', + Ready = 'Server Ready', + Error = 'Server Error', +} + export const statusBar: StatusBar = new StatusBar(); diff --git a/src/helpers/file-helpers.ts b/src/utils/file-utils.ts similarity index 100% rename from src/helpers/file-helpers.ts rename to src/utils/file-utils.ts