diff --git a/package-lock.json b/package-lock.json index 04083292..fdaf501c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,6 +13,7 @@ "globby": "^11.0.4", "ini": "^1.3.8", "lodash": "^4.17.21", + "uuid": "^8.3.2", "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-uri": "^3.0.2", @@ -27,6 +28,7 @@ "@types/lodash": "^4.14.171", "@types/mocha": "^8.2.3", "@types/node": "^14.17.5", + "@types/uuid": "^8.3.1", "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^4.28.3", "@typescript-eslint/parser": "^4.28.3", @@ -34,6 +36,7 @@ "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", "mocha": "^8.4.0", + "prettier": "^2.4.1", "rimraf": "^3.0.2", "ts-node": "^9.1.1", "typescript": "^4.3.5" @@ -53,9 +56,9 @@ } }, "node_modules/@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true, "engines": { "node": ">=6.9.0" @@ -224,9 +227,9 @@ } }, "node_modules/@types/chai": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz", - "integrity": "sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==", + "version": "4.2.22", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", + "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==", "dev": true }, "node_modules/@types/ini": { @@ -236,15 +239,15 @@ "dev": true }, "node_modules/@types/json-schema": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", - "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "node_modules/@types/lodash": { - "version": "4.14.171", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", - "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", + "version": "4.14.174", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.174.tgz", + "integrity": "sha512-KMBLT6+g9qrGXpDt7ohjWPUD34WA/jasrtjTEHStF0NPdEwJ1N9SZ+4GaMVDeuk/y0+X5j9xFm6mNiXS7UoaLQ==", "dev": true }, "node_modules/@types/mocha": { @@ -254,25 +257,31 @@ "dev": true }, "node_modules/@types/node": { - "version": "14.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.6.tgz", - "integrity": "sha512-iBxsxU7eswQDGhlr3AiamBxOssaYxbM+NKXVil8jg9yFXvrfEFbDumLD/2dMTB+zYyg7w+Xjt8yuxfdbUHAtcQ==", + "version": "14.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.19.tgz", + "integrity": "sha512-jjYI6NkyfXykucU6ELEoT64QyKOdvaA6enOqKtP4xUsGY0X0ZUZz29fUmrTRo+7v7c6TgDu82q3GHHaCEkqZwA==", + "dev": true + }, + "node_modules/@types/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", "dev": true }, "node_modules/@types/vscode": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.58.1.tgz", - "integrity": "sha512-sa76rDXiSif09he8KoaWWUQxsuBr2+uND0xn1GUbEODkuEjp2p7Rqd3t5qlvklfmAedLFdL7MdnsPa57uzwcOw==", + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.60.0.tgz", + "integrity": "sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==", "dev": true }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz", - "integrity": "sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz", + "integrity": "sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA==", "dev": true, "dependencies": { - "@typescript-eslint/experimental-utils": "4.28.5", - "@typescript-eslint/scope-manager": "4.28.5", + "@typescript-eslint/experimental-utils": "4.31.2", + "@typescript-eslint/scope-manager": "4.31.2", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -297,15 +306,15 @@ } }, "node_modules/@typescript-eslint/experimental-utils": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz", - "integrity": "sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", + "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", "dev": true, "dependencies": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.5", - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/typescript-estree": "4.28.5", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" }, @@ -321,14 +330,14 @@ } }, "node_modules/@typescript-eslint/parser": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.5.tgz", - "integrity": "sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", + "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", "dev": true, "dependencies": { - "@typescript-eslint/scope-manager": "4.28.5", - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/typescript-estree": "4.28.5", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "debug": "^4.3.1" }, "engines": { @@ -348,13 +357,13 @@ } }, "node_modules/@typescript-eslint/scope-manager": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz", - "integrity": "sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz", + "integrity": "sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/visitor-keys": "4.28.5" + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2" }, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -365,9 +374,9 @@ } }, "node_modules/@typescript-eslint/types": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.5.tgz", - "integrity": "sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", + "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", "dev": true, "engines": { "node": "^8.10.0 || ^10.13.0 || >=11.10.1" @@ -378,13 +387,13 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz", - "integrity": "sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz", + "integrity": "sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/visitor-keys": "4.28.5", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -405,12 +414,12 @@ } }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz", - "integrity": "sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz", + "integrity": "sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==", "dev": true, "dependencies": { - "@typescript-eslint/types": "4.28.5", + "@typescript-eslint/types": "4.31.2", "eslint-visitor-keys": "^2.0.0" }, "engines": { @@ -594,9 +603,9 @@ "dev": true }, "node_modules/buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "node_modules/callsites": { @@ -638,9 +647,9 @@ } }, "node_modules/chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "dependencies": { "ansi-styles": "^4.1.0", @@ -780,9 +789,9 @@ } }, "node_modules/deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "node_modules/diff": { @@ -857,9 +866,9 @@ } }, "node_modules/eslint": { - "version": "7.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.31.0.tgz", - "integrity": "sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "dependencies": { "@babel/code-frame": "7.12.11", @@ -1119,9 +1128,9 @@ "dev": true }, "node_modules/fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "dependencies": { "reusify": "^1.0.4" } @@ -1188,9 +1197,9 @@ } }, "node_modules/flatted": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", - "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "node_modules/fs.realpath": { @@ -1269,9 +1278,9 @@ } }, "node_modules/globals": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", - "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "dev": true, "dependencies": { "type-fest": "^0.20.2" @@ -1422,9 +1431,9 @@ } }, "node_modules/is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.2.tgz", + "integrity": "sha512-ZZTOjRcDjuAAAv2cTBQP/lL59ZTArx77+7UzHdWW/XB1mrfp7DEaVpKmZ0XIzx+M7AxfhKcqV+nMetUQmFifwg==", "dependencies": { "is-extglob": "^2.1.1" }, @@ -1870,6 +1879,18 @@ "node": ">= 0.8.0" } }, + "node_modules/prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "dev": true, + "bin": { + "prettier": "bin-prettier.js" + }, + "engines": { + "node": ">=10.13.0" + } + }, "node_modules/progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -2113,9 +2134,9 @@ } }, "node_modules/source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", @@ -2129,26 +2150,26 @@ "dev": true }, "node_modules/string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "dependencies": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" @@ -2196,9 +2217,9 @@ } }, "node_modules/table/node_modules/ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "dependencies": { "fast-deep-equal": "^3.1.1", @@ -2324,9 +2345,9 @@ } }, "node_modules/typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true, "bin": { "tsc": "bin/tsc", @@ -2345,6 +2366,14 @@ "punycode": "^2.1.0" } }, + "node_modules/uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", + "bin": { + "uuid": "dist/bin/uuid" + } + }, "node_modules/v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", @@ -2597,9 +2626,9 @@ } }, "@babel/helper-validator-identifier": { - "version": "7.14.8", - "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.14.8.tgz", - "integrity": "sha512-ZGy6/XQjllhYQrNw/3zfWRwZCTVSiBLZ9DHVZxn9n2gip/7ab8mv2TWlKPIBk26RwedCBoWdjLmn+t9na2Gcow==", + "version": "7.15.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.15.7.tgz", + "integrity": "sha512-K4JvCtQqad9OY2+yTU8w+E82ywk/fe+ELNlt1G8z3bVGlZfn/hOcQQsUhGhW/N+tb3fxK800wLtKOE/aM0m72w==", "dev": true }, "@babel/highlight": { @@ -2734,9 +2763,9 @@ } }, "@types/chai": { - "version": "4.2.21", - "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.21.tgz", - "integrity": "sha512-yd+9qKmJxm496BOV9CMNaey8TWsikaZOwMRwPHQIjcOJM9oV+fi9ZMNw3JsVnbEEbo2gRTDnGEBv8pjyn67hNg==", + "version": "4.2.22", + "resolved": "https://registry.npmjs.org/@types/chai/-/chai-4.2.22.tgz", + "integrity": "sha512-tFfcE+DSTzWAgifkjik9AySNqIyNoYwmR+uecPwwD/XRNfvOjmC/FjCxpiUGDkDVDphPfCUecSQVFw+lN3M3kQ==", "dev": true }, "@types/ini": { @@ -2746,15 +2775,15 @@ "dev": true }, "@types/json-schema": { - "version": "7.0.8", - "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.8.tgz", - "integrity": "sha512-YSBPTLTVm2e2OoQIDYx8HaeWJ5tTToLH67kXR7zYNGupXMEHa2++G8k+DczX2cFVgalypqtyZIcU19AFcmOpmg==", + "version": "7.0.9", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.9.tgz", + "integrity": "sha512-qcUXuemtEu+E5wZSJHNxUXeCZhAfXKQ41D+duX+VYPde7xyEVZci+/oXKJL13tnRs9lR2pr4fod59GT6/X1/yQ==", "dev": true }, "@types/lodash": { - "version": "4.14.171", - "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.171.tgz", - "integrity": "sha512-7eQ2xYLLI/LsicL2nejW9Wyko3lcpN6O/z0ZLHrEQsg280zIdCv1t/0m6UtBjUHokCGBQ3gYTbHzDkZ1xOBwwg==", + "version": "4.14.174", + "resolved": "https://registry.npmjs.org/@types/lodash/-/lodash-4.14.174.tgz", + "integrity": "sha512-KMBLT6+g9qrGXpDt7ohjWPUD34WA/jasrtjTEHStF0NPdEwJ1N9SZ+4GaMVDeuk/y0+X5j9xFm6mNiXS7UoaLQ==", "dev": true }, "@types/mocha": { @@ -2764,25 +2793,31 @@ "dev": true }, "@types/node": { - "version": "14.17.6", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.6.tgz", - "integrity": "sha512-iBxsxU7eswQDGhlr3AiamBxOssaYxbM+NKXVil8jg9yFXvrfEFbDumLD/2dMTB+zYyg7w+Xjt8yuxfdbUHAtcQ==", + "version": "14.17.19", + "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.19.tgz", + "integrity": "sha512-jjYI6NkyfXykucU6ELEoT64QyKOdvaA6enOqKtP4xUsGY0X0ZUZz29fUmrTRo+7v7c6TgDu82q3GHHaCEkqZwA==", + "dev": true + }, + "@types/uuid": { + "version": "8.3.1", + "resolved": "https://registry.npmjs.org/@types/uuid/-/uuid-8.3.1.tgz", + "integrity": "sha512-Y2mHTRAbqfFkpjldbkHGY8JIzRN6XqYRliG8/24FcHm2D2PwW24fl5xMRTVGdrb7iMrwCaIEbLWerGIkXuFWVg==", "dev": true }, "@types/vscode": { - "version": "1.58.1", - "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.58.1.tgz", - "integrity": "sha512-sa76rDXiSif09he8KoaWWUQxsuBr2+uND0xn1GUbEODkuEjp2p7Rqd3t5qlvklfmAedLFdL7MdnsPa57uzwcOw==", + "version": "1.60.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.60.0.tgz", + "integrity": "sha512-wZt3VTmzYrgZ0l/3QmEbCq4KAJ71K3/hmMQ/nfpv84oH8e81KKwPEoQ5v8dNCxfHFVJ1JabHKmCvqdYOoVm1Ow==", "dev": true }, "@typescript-eslint/eslint-plugin": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.28.5.tgz", - "integrity": "sha512-m31cPEnbuCqXtEZQJOXAHsHvtoDi9OVaeL5wZnO2KZTnkvELk+u6J6jHg+NzvWQxk+87Zjbc4lJS4NHmgImz6Q==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-4.31.2.tgz", + "integrity": "sha512-w63SCQ4bIwWN/+3FxzpnWrDjQRXVEGiTt9tJTRptRXeFvdZc/wLiz3FQUwNQ2CVoRGI6KUWMNUj/pk63noUfcA==", "dev": true, "requires": { - "@typescript-eslint/experimental-utils": "4.28.5", - "@typescript-eslint/scope-manager": "4.28.5", + "@typescript-eslint/experimental-utils": "4.31.2", + "@typescript-eslint/scope-manager": "4.31.2", "debug": "^4.3.1", "functional-red-black-tree": "^1.0.1", "regexpp": "^3.1.0", @@ -2791,55 +2826,55 @@ } }, "@typescript-eslint/experimental-utils": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.28.5.tgz", - "integrity": "sha512-bGPLCOJAa+j49hsynTaAtQIWg6uZd8VLiPcyDe4QPULsvQwLHGLSGKKcBN8/lBxIX14F74UEMK2zNDI8r0okwA==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", + "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", "dev": true, "requires": { "@types/json-schema": "^7.0.7", - "@typescript-eslint/scope-manager": "4.28.5", - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/typescript-estree": "4.28.5", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "eslint-scope": "^5.1.1", "eslint-utils": "^3.0.0" } }, "@typescript-eslint/parser": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.28.5.tgz", - "integrity": "sha512-NPCOGhTnkXGMqTznqgVbA5LqVsnw+i3+XA1UKLnAb+MG1Y1rP4ZSK9GX0kJBmAZTMIktf+dTwXToT6kFwyimbw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", + "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", "dev": true, "requires": { - "@typescript-eslint/scope-manager": "4.28.5", - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/typescript-estree": "4.28.5", + "@typescript-eslint/scope-manager": "4.31.2", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/typescript-estree": "4.31.2", "debug": "^4.3.1" } }, "@typescript-eslint/scope-manager": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.28.5.tgz", - "integrity": "sha512-PHLq6n9nTMrLYcVcIZ7v0VY1X7dK309NM8ya9oL/yG8syFINIMHxyr2GzGoBYUdv3NUfCOqtuqps0ZmcgnZTfQ==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-4.31.2.tgz", + "integrity": "sha512-2JGwudpFoR/3Czq6mPpE8zBPYdHWFGL6lUNIGolbKQeSNv4EAiHaR5GVDQaLA0FwgcdcMtRk+SBJbFGL7+La5w==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/visitor-keys": "4.28.5" + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2" } }, "@typescript-eslint/types": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.28.5.tgz", - "integrity": "sha512-MruOu4ZaDOLOhw4f/6iudyks/obuvvZUAHBDSW80Trnc5+ovmViLT2ZMDXhUV66ozcl6z0LJfKs1Usldgi/WCA==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", + "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", "dev": true }, "@typescript-eslint/typescript-estree": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.28.5.tgz", - "integrity": "sha512-FzJUKsBX8poCCdve7iV7ShirP8V+ys2t1fvamVeD1rWpiAnIm550a+BX/fmTHrjEpQJ7ZAn+Z7ZZwJjytk9rZw==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-4.31.2.tgz", + "integrity": "sha512-ieBq8U9at6PvaC7/Z6oe8D3czeW5d//Fo1xkF/s9394VR0bg/UaMYPdARiWyKX+lLEjY3w/FNZJxitMsiWv+wA==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.5", - "@typescript-eslint/visitor-keys": "4.28.5", + "@typescript-eslint/types": "4.31.2", + "@typescript-eslint/visitor-keys": "4.31.2", "debug": "^4.3.1", "globby": "^11.0.3", "is-glob": "^4.0.1", @@ -2848,12 +2883,12 @@ } }, "@typescript-eslint/visitor-keys": { - "version": "4.28.5", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.28.5.tgz", - "integrity": "sha512-dva/7Rr+EkxNWdJWau26xU/0slnFlkh88v3TsyTgRS/IIYFi5iIfpCFM4ikw0vQTFUR9FYSSyqgK4w64gsgxhg==", + "version": "4.31.2", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-4.31.2.tgz", + "integrity": "sha512-PrBId7EQq2Nibns7dd/ch6S6/M4/iwLM9McbgeEbCXfxdwRUNxJ4UNreJ6Gh3fI2GNKNrWnQxKL7oCPmngKBug==", "dev": true, "requires": { - "@typescript-eslint/types": "4.28.5", + "@typescript-eslint/types": "4.31.2", "eslint-visitor-keys": "^2.0.0" } }, @@ -2988,9 +3023,9 @@ "dev": true }, "buffer-from": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.1.tgz", - "integrity": "sha512-MQcXEUbCKtEo7bhqEs6560Hyd4XaovZlO/k9V3hjVUF/zwW7KBVdSK4gIt/bzwS9MbR5qob+F5jusZsb0YQK2A==", + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", + "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "callsites": { @@ -3020,9 +3055,9 @@ } }, "chalk": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.1.tgz", - "integrity": "sha512-diHzdDKxcU+bAsUboHLPEDQiw0qEe0qd7SYUn3HgcFlWgbDcfLGswOHYeGrHKzG9z6UYf01d9VFMfZxPM1xZSg==", + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, "requires": { "ansi-styles": "^4.1.0", @@ -3125,9 +3160,9 @@ } }, "deep-is": { - "version": "0.1.3", - "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.3.tgz", - "integrity": "sha1-s2nW+128E+7PUk+RsHD+7cNXzzQ=", + "version": "0.1.4", + "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", + "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true }, "diff": { @@ -3181,9 +3216,9 @@ "dev": true }, "eslint": { - "version": "7.31.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.31.0.tgz", - "integrity": "sha512-vafgJpSh2ia8tnTkNUkwxGmnumgckLh5aAbLa1xRmIn9+owi8qBNGKL+B881kNKNTy7FFqTEkpNkUvmw0n6PkA==", + "version": "7.32.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-7.32.0.tgz", + "integrity": "sha512-VHZ8gX+EDfz+97jGcgyGCyRia/dPOd6Xh9yPv8Bl1+SoaIwD+a/vlrOmGRUyOYu7MwUhc7CxqeaDZU13S4+EpA==", "dev": true, "requires": { "@babel/code-frame": "7.12.11", @@ -3381,9 +3416,9 @@ "dev": true }, "fastq": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.11.1.tgz", - "integrity": "sha512-HOnr8Mc60eNYl1gzwp6r5RoUyAn5/glBolUzP/Ez6IFVPMPirxn/9phgL6zhOtaTy7ISwPvQ+wT+hfcRZh/bzw==", + "version": "1.13.0", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", + "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", "requires": { "reusify": "^1.0.4" } @@ -3432,9 +3467,9 @@ } }, "flatted": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.1.tgz", - "integrity": "sha512-OMQjaErSFHmHqZe+PSidH5n8j3O0F2DdnVh8JB4j4eUQ2k6KvB0qGfrKIhapvez5JerBbmWkaLYUYWISaESoXg==", + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", + "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", "dev": true }, "fs.realpath": { @@ -3491,9 +3526,9 @@ } }, "globals": { - "version": "13.10.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.10.0.tgz", - "integrity": "sha512-piHC3blgLGFjvOuMmWZX60f+na1lXFDhQXBf1UYp2fXPXqvEUbOhNwi6BsQ0bQishwedgnjkwv1d9zKf+MWw3g==", + "version": "13.11.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", + "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", "dev": true, "requires": { "type-fest": "^0.20.2" @@ -3601,9 +3636,9 @@ "dev": true }, "is-glob": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.1.tgz", - "integrity": "sha512-5G0tKtBTFImOqDnLB2hG6Bp2qcKEFduo4tZu9MT/H6NQv/ghhy30o55ufafxJ/LdH79LLs2Kfrn85TLKyA7BUg==", + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.2.tgz", + "integrity": "sha512-ZZTOjRcDjuAAAv2cTBQP/lL59ZTArx77+7UzHdWW/XB1mrfp7DEaVpKmZ0XIzx+M7AxfhKcqV+nMetUQmFifwg==", "requires": { "is-extglob": "^2.1.1" } @@ -3938,6 +3973,12 @@ "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true }, + "prettier": { + "version": "2.4.1", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.4.1.tgz", + "integrity": "sha512-9fbDAXSBcc6Bs1mZrDYb3XKzDLm4EXXL9sC1LqKP5rZkT6KRr/rf9amVUcODVXgguK/isJz0d0hP72WeaKWsvA==", + "dev": true + }, "progress": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", @@ -4081,9 +4122,9 @@ "dev": true }, "source-map-support": { - "version": "0.5.19", - "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.19.tgz", - "integrity": "sha512-Wonm7zOCIJzBGQdB+thsPar0kYuCIzYvxZwlBa87yi/Mdjv7Tip2cyVbLj5o0cFPN4EVkuTwb3GDDyUx2DGnGw==", + "version": "0.5.20", + "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.20.tgz", + "integrity": "sha512-n1lZZ8Ve4ksRqizaBQgxXDgKwttHDhyfQjA6YZZn8+AroHbsIz+JjwxQDxbp+7y5OYCI8t1Yk7etjD9CRd2hIw==", "dev": true, "requires": { "buffer-from": "^1.0.0", @@ -4097,23 +4138,23 @@ "dev": true }, "string-width": { - "version": "4.2.2", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.2.tgz", - "integrity": "sha512-XBJbT3N4JhVumXE0eoLU9DCjcaF92KLNqTmFCnG1pf8duUxFGwtP6AD6nkjw9a3IdiRtL3E2w3JDiE/xi3vOeA==", + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, "requires": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", - "strip-ansi": "^6.0.0" + "strip-ansi": "^6.0.1" } }, "strip-ansi": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.0.tgz", - "integrity": "sha512-AuvKTrTfQNYNIctbR1K/YGTR1756GycPsg7b9bdV9Duqur4gv6aKqHXah67Z8ImS7WEz5QVcOtlfW2rZEugt6w==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, "requires": { - "ansi-regex": "^5.0.0" + "ansi-regex": "^5.0.1" } }, "strip-json-comments": { @@ -4146,9 +4187,9 @@ }, "dependencies": { "ajv": { - "version": "8.6.2", - "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.2.tgz", - "integrity": "sha512-9807RlWAgT564wT+DjeyU5OFMPjmzxVobvDFmNAhY+5zD6A2ly3jDp6sgnfyDtlIQ+7H97oc/DGCzzfu9rjw9w==", + "version": "8.6.3", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.6.3.tgz", + "integrity": "sha512-SMJOdDP6LqTkD0Uq8qLi+gMwSt0imXLSV080qFVwJCpH9U6Mb+SUGHAXM0KNbcBPguytWyvFxcHgMLe2D2XSpw==", "dev": true, "requires": { "fast-deep-equal": "^3.1.1", @@ -4238,9 +4279,9 @@ "dev": true }, "typescript": { - "version": "4.3.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.3.5.tgz", - "integrity": "sha512-DqQgihaQ9cUrskJo9kIyW/+g0Vxsk8cDtZ52a3NGh0YNTfpUSArXSohyUGnvbPazEPLu398C0UxmKSOrPumUzA==", + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", + "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", "dev": true }, "uri-js": { @@ -4252,6 +4293,11 @@ "punycode": "^2.1.0" } }, + "uuid": { + "version": "8.3.2", + "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", + "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==" + }, "v8-compile-cache": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", diff --git a/package.json b/package.json index 55722d11..e39dfd8f 100644 --- a/package.json +++ b/package.json @@ -45,6 +45,7 @@ "globby": "^11.0.4", "ini": "^1.3.8", "lodash": "^4.17.21", + "uuid": "^8.3.2", "vscode-languageserver": "^7.0.0", "vscode-languageserver-textdocument": "^1.0.1", "vscode-uri": "^3.0.2", @@ -56,6 +57,7 @@ "@types/lodash": "^4.14.171", "@types/mocha": "^8.2.3", "@types/node": "^14.17.5", + "@types/uuid": "^8.3.1", "@types/vscode": "^1.56.0", "@typescript-eslint/eslint-plugin": "^4.28.3", "@typescript-eslint/parser": "^4.28.3", @@ -63,6 +65,7 @@ "eslint": "^7.30.0", "eslint-config-prettier": "^8.3.0", "mocha": "^8.4.0", + "prettier": "^2.4.1", "rimraf": "^3.0.2", "ts-node": "^9.1.1", "typescript": "^4.3.5" diff --git a/src/interfaces/extensionSettings.ts b/src/interfaces/extensionSettings.ts index c9e6d3b8..9ddcdff4 100644 --- a/src/interfaces/extensionSettings.ts +++ b/src/interfaces/extensionSettings.ts @@ -1,5 +1,17 @@ +export type IContainerEngine = 'auto' | 'podman' | 'docker'; + +export type IPullPolicy = 'always' | 'missing' | 'never' | 'tag'; + export interface ExtensionSettings { ansible: { path: string; useFullyQualifiedCollectionNames: boolean }; ansibleLint: { enabled: boolean; path: string; arguments: string }; + executionEnvironment: ExecutionEnvironmentSettings; python: { interpreterPath: string; activationScript: string }; } + +interface ExecutionEnvironmentSettings { + containerEngine: IContainerEngine; + enabled: boolean; + image: string; + pullPolicy: IPullPolicy; +} diff --git a/src/services/ansibleConfig.ts b/src/services/ansibleConfig.ts index 40848107..e64a555c 100644 --- a/src/services/ansibleConfig.ts +++ b/src/services/ansibleConfig.ts @@ -1,11 +1,9 @@ -import * as child_process from 'child_process'; import * as ini from 'ini'; import * as _ from 'lodash'; import * as path from 'path'; -import { URI } from 'vscode-uri'; import { Connection } from 'vscode-languageserver'; -import { withInterpreter } from '../utils/misc'; import { WorkspaceFolderContext } from './workspaceManager'; +import { CommandRunner } from '../utils/commandRunner'; export class AnsibleConfig { private connection: Connection; @@ -25,21 +23,19 @@ export class AnsibleConfig { this.context.workspaceFolder.uri ); - // get Ansible configuration - const [ansibleConfigCommand, ansibleConfigEnv] = withInterpreter( - `${settings.ansible.path}-config`, - 'dump', - settings.python.interpreterPath, - settings.python.activationScript + const commandRunner = new CommandRunner( + this.connection, + this.context, + settings ); - const ansibleConfigResult = child_process.execSync(ansibleConfigCommand, { - encoding: 'utf-8', - cwd: URI.parse(this.context.workspaceFolder.uri).path, - env: ansibleConfigEnv, - }); + // get Ansible configuration + const ansibleConfigResult = await commandRunner.runCommand( + 'ansible-config', + 'dump' + ); - let config = ini.parse(ansibleConfigResult); + let config = ini.parse(ansibleConfigResult.stdout); config = _.mapKeys( config, (_, key) => key.substring(0, key.indexOf('(')) // remove config source in parenthesis @@ -47,19 +43,12 @@ export class AnsibleConfig { this._collection_paths = parsePythonStringArray(config.COLLECTIONS_PATHS); // get Ansible basic information - const [ansibleCommand, ansibleEnv] = withInterpreter( - `${settings.ansible.path}`, - '--version', - settings.python.interpreterPath, - settings.python.activationScript + const ansibleVersionResult = await commandRunner.runCommand( + 'ansible', + '--version' ); - const ansibleVersionResult = child_process.execSync(ansibleCommand, { - encoding: 'utf-8', - env: ansibleEnv, - }); - - const versionInfo = ini.parse(ansibleVersionResult); + const versionInfo = ini.parse(ansibleVersionResult.stdout); this._module_locations = parsePythonStringArray( versionInfo['configured module search path'] ); @@ -71,18 +60,13 @@ export class AnsibleConfig { // get Python sys.path // this is needed to get the pre-installed collections to work - const [pythonPathCommand, pythonPathEnv] = withInterpreter( + const pythonPathResult = await commandRunner.runCommand( 'python3', - ' -c "import sys; print(sys.path, end=\\"\\")"', - settings.python.interpreterPath, - settings.python.activationScript + ' -c "import sys; print(sys.path, end=\\"\\")"' + ); + this._collection_paths.push( + ...parsePythonStringArray(pythonPathResult.stdout) ); - - const pythonPathResult = child_process.execSync(pythonPathCommand, { - encoding: 'utf-8', - env: pythonPathEnv, - }); - this._collection_paths.push(...parsePythonStringArray(pythonPathResult)); } catch (error) { if (error instanceof Error) { this.connection.window.showErrorMessage(error.message); @@ -94,10 +78,18 @@ export class AnsibleConfig { } } + set collections_paths(updatedCollectionPath: string[]) { + this._collection_paths = updatedCollectionPath; + } + get collections_paths(): string[] { return this._collection_paths; } + set module_locations(updatedModulesPath: string[]) { + this._module_locations = updatedModulesPath; + } + get module_locations(): string[] { return this._module_locations; } diff --git a/src/services/docsLibrary.ts b/src/services/docsLibrary.ts index 6e5ced0b..aab32ac9 100644 --- a/src/services/docsLibrary.ts +++ b/src/services/docsLibrary.ts @@ -1,3 +1,4 @@ +import { Connection } from 'vscode-languageserver'; import { Node } from 'yaml/types'; import { getDeclaredCollections } from '../utils/yaml'; import { findDocumentation, findPluginRouting } from '../utils/docsFinder'; @@ -13,6 +14,7 @@ import { } from '../utils/docsParser'; import { IModuleMetadata } from '../interfaces/module'; export class DocsLibrary { + private connection: Connection; private modules = new Map(); private _moduleFqcns = new Set(); private docFragments = new Map(); @@ -22,55 +24,73 @@ export class DocsLibrary { IPluginRoutesByType >(); - constructor(context: WorkspaceFolderContext) { + constructor(connection: Connection, context: WorkspaceFolderContext) { + this.connection = connection; this.context = context; } public async initialize(): Promise { - const ansibleConfig = await this.context.ansibleConfig; - for (const modulesPath of ansibleConfig.module_locations) { - (await findDocumentation(modulesPath, 'builtin')).forEach((doc) => { - this.modules.set(doc.fqcn, doc); - this.moduleFqcns.add(doc.fqcn); - }); - - (await findDocumentation(modulesPath, 'builtin_doc_fragment')).forEach( - (doc) => { - this.docFragments.set(doc.fqcn, doc); - } + try { + const settings = await this.context.documentSettings.get( + this.context.workspaceFolder.uri ); - } - - ( - await findPluginRouting(ansibleConfig.ansible_location, 'builtin') - ).forEach((r, collection) => this.pluginRouting.set(collection, r)); - - for (const collectionsPath of ansibleConfig.collections_paths) { - (await findDocumentation(collectionsPath, 'collection')).forEach( - (doc) => { + const ansibleConfig = await this.context.ansibleConfig; + if (settings.executionEnvironment.enabled) { + // ensure plugin/module cache is established + await this.context.executionEnvironment; + } + for (const modulesPath of ansibleConfig.module_locations) { + (await findDocumentation(modulesPath, 'builtin')).forEach((doc) => { this.modules.set(doc.fqcn, doc); this.moduleFqcns.add(doc.fqcn); - } - ); + }); + + (await findDocumentation(modulesPath, 'builtin_doc_fragment')).forEach( + (doc) => { + this.docFragments.set(doc.fqcn, doc); + } + ); + } ( - await findDocumentation(collectionsPath, 'collection_doc_fragment') - ).forEach((doc) => { - this.docFragments.set(doc.fqcn, doc); - }); + await findPluginRouting(ansibleConfig.ansible_location, 'builtin') + ).forEach((r, collection) => this.pluginRouting.set(collection, r)); + + for (const collectionsPath of ansibleConfig.collections_paths) { + (await findDocumentation(collectionsPath, 'collection')).forEach( + (doc) => { + this.modules.set(doc.fqcn, doc); + this.moduleFqcns.add(doc.fqcn); + } + ); - (await findPluginRouting(collectionsPath, 'collection')).forEach( - (r, collection) => this.pluginRouting.set(collection, r) - ); + ( + await findDocumentation(collectionsPath, 'collection_doc_fragment') + ).forEach((doc) => { + this.docFragments.set(doc.fqcn, doc); + }); + + (await findPluginRouting(collectionsPath, 'collection')).forEach( + (r, collection) => this.pluginRouting.set(collection, r) + ); - // add all valid redirect routes as possible FQCNs - for (const [collection, routesByType] of this.pluginRouting) { - for (const [name, route] of routesByType.get('modules') || []) { - if (route.redirect && !route.tombstone) { - this.moduleFqcns.add(`${collection}.${name}`); + // add all valid redirect routes as possible FQCNs + for (const [collection, routesByType] of this.pluginRouting) { + for (const [name, route] of routesByType.get('modules') || []) { + if (route.redirect && !route.tombstone) { + this.moduleFqcns.add(`${collection}.${name}`); + } } } } + } catch (error) { + if (error instanceof Error) { + this.connection.window.showErrorMessage(error.message); + } else { + this.connection.console.error( + `Exception in DocsLibrary service: ${JSON.stringify(error)}` + ); + } } } diff --git a/src/services/executionEnvironment.ts b/src/services/executionEnvironment.ts new file mode 100644 index 00000000..1a0230c0 --- /dev/null +++ b/src/services/executionEnvironment.ts @@ -0,0 +1,325 @@ +import * as child_process from 'child_process'; +import * as fs from 'fs'; +import * as path from 'path'; +import { URI } from 'vscode-uri'; +import { Connection } from 'vscode-languageserver'; +import { v4 as uuidv4 } from 'uuid'; +import { ImagePuller } from '../utils/imagePuller'; +import { asyncExec } from '../utils/misc'; +import { WorkspaceFolderContext } from './workspaceManager'; +import { IContainerEngine } from '../interfaces/extensionSettings'; + +export class ExecutionEnvironment { + private connection: Connection; + private context: WorkspaceFolderContext; + private useProgressTracker = false; + private _container_engine: IContainerEngine; + private _container_image: string; + private _container_image_id: string; + + constructor(connection: Connection, context: WorkspaceFolderContext) { + this.connection = connection; + this.context = context; + this.useProgressTracker = + !!context.clientCapabilities.window?.workDoneProgress; + } + + public async initialize(): Promise { + try { + const settings = await this.context.documentSettings.get( + this.context.workspaceFolder.uri + ); + this._container_image = settings.executionEnvironment.image; + this._container_engine = settings.executionEnvironment.containerEngine; + if (this._container_engine === 'auto') { + for (const ce of ['podman', 'docker']) { + try { + child_process.execSync(`which ${ce}`, { + encoding: 'utf-8', + }); + } catch (error) { + this.connection.console.info(`Container engine '${ce}' not found`); + continue; + } + this._container_engine = ce; + this.connection.console.log(`Container engine set to: '${ce}'`); + break; + } + } else { + try { + child_process.execSync(`which ${this._container_engine}`, { + encoding: 'utf-8', + }); + } catch (error) { + this.connection.window.showErrorMessage( + `Container engine '${this._container_engine}' not found. Failed with error '${error}'` + ); + return; + } + } + if (!['podman', 'docker'].includes(this._container_engine)) { + this.connection.window.showInformationMessage( + 'No valid container engine found.' + ); + return; + } + const imagePuller = new ImagePuller( + this.connection, + this.context, + this._container_engine, + this._container_image, + settings.executionEnvironment.pullPolicy + ); + const setupDone = await imagePuller.setupImage(); + if (!setupDone) { + this.connection.window.showErrorMessage( + `Execution environment image '${this._container_image}' setup failed. + For more details check output console logs for ansible-language-server` + ); + return; + } + this.fetchPluginDocs(); + } catch (error) { + if (error instanceof Error) { + this.connection.window.showErrorMessage(error.message); + } else { + this.connection.console.error( + `Exception in ExecutionEnvironment service: ${JSON.stringify(error)}` + ); + } + } + } + + async fetchPluginDocs(): Promise { + const ansibleConfig = await this.context.ansibleConfig; + const containerName = `${this._container_image.replace( + /[^a-z0-9]/gi, + '_' + )}`; + let progressTracker; + + try { + const containerImageIdCommand = `${this._container_engine} images ${this._container_image} --format="{{.ID}}" | head -n 1`; + this.connection.console.log(containerImageIdCommand); + this._container_image_id = child_process + .execSync(containerImageIdCommand, { + encoding: 'utf-8', + }) + .trim(); + const hostCacheBasePath = path.resolve( + `${process.env.HOME}/.cache/ansible-language-server/${containerName}/${this._container_image_id}` + ); + + const isContainerRunning = this.runContainer(containerName); + if (!isContainerRunning) { + return; + } + + if (fs.existsSync(hostCacheBasePath)) { + ansibleConfig.collections_paths = this.updateCachePaths( + ansibleConfig.collections_paths, + hostCacheBasePath + ); + ansibleConfig.module_locations = this.updateCachePaths( + ansibleConfig.module_locations, + hostCacheBasePath + ); + } else { + if (this.useProgressTracker) { + progressTracker = await this.connection.window.createWorkDoneProgress(); + } + if (progressTracker) { + progressTracker.begin( + 'execution-environment', + undefined, + `Copy plugin docs from '${this._container_image} to host cache path`, + true + ); + } + ansibleConfig.collections_paths = await this.copyPluginDocFiles( + hostCacheBasePath, + containerName, + ansibleConfig.collections_paths, + '**/ansible_collections' + ); + + const builtin_plugin_locations: string[] = []; + ansibleConfig.module_locations.forEach((modulePath) => { + const pluginsPathParts = modulePath.split(path.sep).slice(0, -1); + if (pluginsPathParts.includes('site-packages')) { + // ansible-config returns default builtin configured module path + // as ``/site-packages/ansible/modules`` to copy other plugins + // to local cache strip the ``modules`` part from the path and append + // ``plugins`` folder. + pluginsPathParts.push('plugins'); + } + builtin_plugin_locations.push(pluginsPathParts.join(path.sep)); + }); + // Copy builtin plugins + await this.copyPluginDocFiles( + hostCacheBasePath, + containerName, + builtin_plugin_locations, + '*' + ); + + // Copy builtin modules + ansibleConfig.module_locations = await this.copyPluginDocFiles( + hostCacheBasePath, + containerName, + ansibleConfig.module_locations, + '**/modules' + ); + } + } catch (error) { + this.connection.window.showErrorMessage( + `Exception in ExecutionEnvironment service while fetching docs: ${JSON.stringify( + error + )}` + ); + } finally { + if (progressTracker) { + progressTracker.done(); + } + this.cleanUpContainer(containerName); + } + } + + public wrapContainerArgs(command: string): string { + const workspaceFolderPath = URI.parse( + this.context.workspaceFolder.uri + ).path; + const containerCommand: Array = [this._container_engine]; + containerCommand.push(...['run', '--rm']); + containerCommand.push(...['--workdir', workspaceFolderPath]); + + containerCommand.push( + ...['-v', `${workspaceFolderPath}:${workspaceFolderPath}`] + ); + if (this._container_engine === 'podman') { + // container namespace stuff + containerCommand.push('--group-add=root'); + containerCommand.push('--ipc=host'); + + // docker does not support this option + containerCommand.push('--quiet'); + } else { + containerCommand.push(`--user=${process.getuid()}`); + } + containerCommand.push(`--name ansible_language_server_${uuidv4()}`); + containerCommand.push(this._container_image); + containerCommand.push(command); + const generatedCommand = containerCommand.join(' '); + this.connection.console.log( + `container engine invocation: ${generatedCommand}` + ); + return generatedCommand; + } + + public cleanUpContainer(containerName: string): void { + [ + `${this._container_engine} stop ${containerName}`, + `${this._container_engine} rm ${containerName}`, + ].forEach((command) => { + try { + child_process.execSync(command, { + cwd: URI.parse(this.context.workspaceFolder.uri).path, + }); + } catch (error) { + // container already stopped and/or removed + } + }); + } + + private isPluginInPath( + containerName: string, + searchPath: string, + pluginFolderPath: string + ): boolean { + const command = `${this._container_engine} exec ${containerName} find ${searchPath} -path '${pluginFolderPath}'`; + try { + this.connection.console.info(`Executing command ${command}`); + const result = child_process + .execSync(command, { + encoding: 'utf-8', + }) + .trim(); + return result !== ''; + } catch (error) { + this.connection.console.error(error); + return false; + } + } + + private runContainer(containerName: string): boolean { + // ensure container is not running + this.cleanUpContainer(containerName); + try { + const command = `${this._container_engine} run --rm -it -d --name ${containerName} ${this._container_image} bash`; + this.connection.console.log(`run container with command '${command}'`); + child_process.execSync(command, { + encoding: 'utf-8', + }); + } catch (error) { + this.connection.window.showErrorMessage( + `Failed to initialize execution environment '${this._container_image}': ${error}` + ); + return false; + } + return true; + } + + private async copyPluginDocFiles( + hostPluginDocCacheBasePath: string, + containerName: string, + containerPluginPaths: string[], + searchKind: string + ): Promise { + const updatedHostDocPath: string[] = []; + if (fs.existsSync(hostPluginDocCacheBasePath)) { + containerPluginPaths.forEach((srcPath) => { + updatedHostDocPath.push(path.join(hostPluginDocCacheBasePath, srcPath)); + }); + } else { + containerPluginPaths.forEach((srcPath) => { + if ( + srcPath === '' || + !this.isPluginInPath(containerName, srcPath, searchKind) + ) { + return; + } + const destPath = path.join(hostPluginDocCacheBasePath, srcPath); + const destPathFolder = destPath + .split(path.sep) + .slice(0, -1) + .join(path.sep); + fs.mkdirSync(destPath, { recursive: true }); + const copyCommand = `docker cp ${containerName}:${srcPath} ${destPathFolder}`; + this.connection.console.log( + `Copying plugins from container to local cache path ${copyCommand}` + ); + asyncExec(copyCommand, { + encoding: 'utf-8', + }); + + updatedHostDocPath.push(destPath); + }); + } + + return updatedHostDocPath; + } + + private updateCachePaths( + pluginPaths: string[], + cacheBasePath: string + ): string[] { + const localCachePaths: string[] = []; + pluginPaths.forEach((srcPath) => { + const destPath = path.join(cacheBasePath, srcPath); + if (fs.existsSync(destPath)) { + localCachePaths.push(destPath); + } + }); + return localCachePaths; + } +} diff --git a/src/services/settingsManager.ts b/src/services/settingsManager.ts index fd5ecfc2..1606906e 100644 --- a/src/services/settingsManager.ts +++ b/src/services/settingsManager.ts @@ -16,6 +16,12 @@ export class SettingsManager { ansible: { path: 'ansible', useFullyQualifiedCollectionNames: true }, ansibleLint: { enabled: true, path: 'ansible-lint', arguments: '' }, python: { interpreterPath: '', activationScript: '' }, + executionEnvironment: { + containerEngine: 'auto', + enabled: false, + image: 'quay.io/ansible/ansible-navigator-demo-ee:0.6.0', + pullPolicy: 'missing' + } }; private globalSettings: ExtensionSettings = this.defaultSettings; diff --git a/src/services/workspaceManager.ts b/src/services/workspaceManager.ts index 495e5c7a..272b281e 100644 --- a/src/services/workspaceManager.ts +++ b/src/services/workspaceManager.ts @@ -9,6 +9,7 @@ import { import { AnsibleConfig } from './ansibleConfig'; import { AnsibleLint } from './ansibleLint'; import { DocsLibrary } from './docsLibrary'; +import { ExecutionEnvironment } from './executionEnvironment'; import { MetadataLibrary } from './metadataLibrary'; import { SettingsManager } from './settingsManager'; @@ -108,6 +109,7 @@ export class WorkspaceFolderContext { public documentSettings: SettingsManager; // Lazy-loading anything that needs this context itself + private _executionEnvironment: Thenable | undefined; private _docsLibrary: Thenable | undefined; private _ansibleConfig: Thenable | undefined; private _ansibleLint: AnsibleLint | undefined; @@ -130,6 +132,7 @@ export class WorkspaceFolderContext { () => { // in case the configuration changes for this folder, we should // invalidate the services that rely on it in initialization + this._executionEnvironment = undefined; this._ansibleConfig = undefined; this._docsLibrary = undefined; } @@ -145,6 +148,7 @@ export class WorkspaceFolderContext { if (fileEvent.uri.startsWith(this.workspaceFolder.uri)) { // in case the configuration changes for this folder, we should // invalidate the services that rely on it in initialization + this._executionEnvironment = undefined; this._ansibleConfig = undefined; this._docsLibrary = undefined; } @@ -153,7 +157,7 @@ export class WorkspaceFolderContext { public get docsLibrary(): Thenable { if (!this._docsLibrary) { - const docsLibrary = new DocsLibrary(this); + const docsLibrary = new DocsLibrary(this.connection, this); this._docsLibrary = docsLibrary.initialize().then(() => docsLibrary); } return this._docsLibrary; @@ -175,4 +179,17 @@ export class WorkspaceFolderContext { } return this._ansibleLint; } + + public get executionEnvironment(): Thenable { + if (!this._executionEnvironment) { + const executionEnvironment = new ExecutionEnvironment( + this.connection, + this + ); + this._executionEnvironment = executionEnvironment + .initialize() + .then(() => executionEnvironment); + } + return this._executionEnvironment; + } } diff --git a/src/utils/commandRunner.ts b/src/utils/commandRunner.ts new file mode 100644 index 00000000..512b7d10 --- /dev/null +++ b/src/utils/commandRunner.ts @@ -0,0 +1,72 @@ +import * as path from 'path'; +import { URI } from 'vscode-uri'; +import { Connection } from 'vscode-languageserver'; +import { withInterpreter, asyncExec } from './misc'; +import { WorkspaceFolderContext } from '../services/workspaceManager'; +import { ExtensionSettings } from '../interfaces/extensionSettings'; + +export class CommandRunner { + private connection: Connection; + private context: WorkspaceFolderContext; + private settings: ExtensionSettings; + + constructor( + connection: Connection, + context: WorkspaceFolderContext, + settings: ExtensionSettings + ) { + this.connection = connection; + this.context = context; + this.settings = settings; + } + + public async runCommand( + executable: string, + args: string, + workingDirectory?: string + ): Promise<{ + stdout: string; + stderr: string; + }> { + let executablePath: string; + let command: string; + let runEnv: NodeJS.ProcessEnv | undefined; + const executionEnvironment = await this.context.executionEnvironment; + const isEEEnabled = this.settings.executionEnvironment.enabled; + const interpreterPath = isEEEnabled + ? 'python3' + : this.settings.python.interpreterPath; + if (executable.startsWith('ansible')) { + executablePath = isEEEnabled + ? executable + : path.join(path.dirname(this.settings.ansible.path), executable); + } else { + executablePath = executable; + } + + // prepare command and env for local run + if (!isEEEnabled) { + [command, runEnv] = withInterpreter( + executablePath, + args, + interpreterPath, + this.settings.python.activationScript + ); + } // prepare command executin env run + else { + command = executionEnvironment.wrapContainerArgs(`${executable} ${args}`); + runEnv = undefined; + } + + const currentWorkingDirectory = workingDirectory + ? workingDirectory + : URI.parse(this.context.workspaceFolder.uri).path; + const result = await asyncExec(command, { + encoding: 'utf-8', + cwd: currentWorkingDirectory, + env: runEnv, + }); + + return result; + } +} diff --git a/src/utils/imagePuller.ts b/src/utils/imagePuller.ts new file mode 100644 index 00000000..f7aea57f --- /dev/null +++ b/src/utils/imagePuller.ts @@ -0,0 +1,110 @@ +import * as child_process from 'child_process'; +import { Connection } from 'vscode-languageserver'; +import { WorkspaceFolderContext } from '../services/workspaceManager'; + +export class ImagePuller { + private connection: Connection; + private context: WorkspaceFolderContext; + private useProgressTracker = false; + private _containerEngine: string; + private _containerImage: string; + private _pullPolicy: string; + + constructor( + connection: Connection, + context: WorkspaceFolderContext, + containerEngine: string, + containerImage: string, + pullPolicy: string + ) { + this.connection = connection; + this.context = context; + this._containerEngine = containerEngine; + this._containerImage = containerImage; + this._pullPolicy = pullPolicy; + this.useProgressTracker = + !!context.clientCapabilities.window?.workDoneProgress; + } + + public async setupImage(): Promise { + let setupComplete = false; + const imageTag = this._containerImage.split(':', 2)[1] || 'latest'; + const imagePresent = this.checkForImage(); + const pullRequired = this.determinePull(imagePresent, imageTag); + + let progressTracker; + if (this.useProgressTracker) { + progressTracker = await this.connection.window.createWorkDoneProgress(); + } + if (pullRequired) { + this.connection.console.log( + `Pulling image '${this._containerImage}' with pull-policy '${this._pullPolicy}' and image-tag '${imageTag}'` + ); + + try { + const pullCommand = `${this._containerEngine} pull ${this._containerImage}`; + if (progressTracker) { + progressTracker.begin( + 'image-puller', + undefined, + `Pulling image '${this._containerImage}'` + ); + } + child_process.execSync(pullCommand, { + encoding: 'utf-8', + }); + this.connection.console.info( + `Container image '${this._containerImage}' pull successful` + ); + setupComplete = true; + } catch (error) { + let errorMsg = `Failed to pull container image ${this._containerEngine} with error '${error}'`; + errorMsg += + 'Check the execution environment image name, connectivity to and permissions for the registry, and try again'; + this.connection.console.error(errorMsg); + setupComplete = false; + } + } else { + setupComplete = true; + } + + if (progressTracker) { + progressTracker.done(); + } + return setupComplete; + } + + private determinePull(imagePresent: boolean, imageTag: string): boolean { + let pull: boolean; + if (this._pullPolicy === 'missing' && !imagePresent) { + pull = true; + } else if (this._pullPolicy === 'always') { + pull = true; + } else if (this._pullPolicy === 'tag' && imageTag === 'latest') { + pull = true; + } else if (this._pullPolicy === 'tag' && !imagePresent) { + pull = true; + } else { + pull = false; + } + return pull; + } + + private checkForImage(): boolean { + try { + const command = `${this._containerEngine} image inspect ${this._containerImage}`; + this.connection.console.log( + `check for container image with command: '${command}'` + ); + child_process.execSync(command, { + encoding: 'utf-8', + }); + return true; + } catch (error) { + this.connection.console.log( + `'${this._containerImage}' image inspection failed, image assumed to be corrupted or missing` + ); + return false; + } + } +} diff --git a/src/utils/misc.ts b/src/utils/misc.ts index 80bebcf5..1337002c 100644 --- a/src/utils/misc.ts +++ b/src/utils/misc.ts @@ -1,5 +1,7 @@ +import * as child_process from 'child_process'; import { promises as fs } from 'fs'; import { URL } from 'url'; +import { promisify } from 'util'; import { TextDocument } from 'vscode-languageserver-textdocument'; import { Range } from 'vscode-languageserver-types'; import * as path from 'path'; @@ -8,6 +10,8 @@ export async function fileExists(fileUri: string): Promise { return !!(await fs.stat(new URL(fileUri)).catch(() => false)); } +export const asyncExec = promisify(child_process.exec); + export function toLspRange( range: [number, number], textDocument: TextDocument