From 218d06dc7d87d527d302fe14706feb6cd6a8d34f Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 25 Sep 2024 18:38:41 +0200 Subject: [PATCH 01/43] Add ProgramFlow-Visualization plugin This commit contains the work of Marko Velten, Matthias Reichenbach and me. The integration should work. However, the visualization and trace generation itself has a lot of bugs left that still need to be fixed. --- package-lock.json | 2740 +++++++++++------ package.json | 26 +- python/deps/pytrace-generator/main.py | 337 ++ src/extension.ts | 14 + src/programflow-visualization/FileHandler.ts | 38 + .../backend/backend.ts | 16 + .../backend/trace_generator.ts | 55 + src/programflow-visualization/constants.ts | 11 + .../frontend/HTMLGenerator.ts | 166 + .../frontend/frontend.ts | 23 + .../frontend/resources/leader-line.min.js | 2 + .../frontend/resources/webview.css | 165 + .../frontend/resources/webview.js | 183 ++ .../frontend/visualization_panel.ts | 277 ++ src/programflow-visualization/main.ts | 45 + src/programflow-visualization/trace_cache.ts | 49 + src/programflow-visualization/types.ts | 52 + 17 files changed, 3177 insertions(+), 1022 deletions(-) create mode 100644 python/deps/pytrace-generator/main.py create mode 100644 src/programflow-visualization/FileHandler.ts create mode 100644 src/programflow-visualization/backend/backend.ts create mode 100644 src/programflow-visualization/backend/trace_generator.ts create mode 100644 src/programflow-visualization/constants.ts create mode 100644 src/programflow-visualization/frontend/HTMLGenerator.ts create mode 100644 src/programflow-visualization/frontend/frontend.ts create mode 100644 src/programflow-visualization/frontend/resources/leader-line.min.js create mode 100644 src/programflow-visualization/frontend/resources/webview.css create mode 100644 src/programflow-visualization/frontend/resources/webview.js create mode 100644 src/programflow-visualization/frontend/visualization_panel.ts create mode 100644 src/programflow-visualization/main.ts create mode 100644 src/programflow-visualization/trace_cache.ts create mode 100644 src/programflow-visualization/types.ts diff --git a/package-lock.json b/package-lock.json index 19214c33..2b92642c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,1255 +1,1774 @@ { "name": "write-your-python-program", - "version": "0.11.2", - "lockfileVersion": 1, + "version": "1.0.6", + "lockfileVersion": 3, "requires": true, - "dependencies": { - "@babel/code-frame": { - "version": "7.12.11", - "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz", - "integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==", - "dev": true, - "requires": { - "@babel/highlight": "^7.10.4" + "packages": { + "": { + "name": "write-your-python-program", + "version": "1.0.6", + "license": "See license in LICENSE", + "dependencies": { + "@types/node": "^18.11.13", + "leader-line": "^1.0.7", + "stringify-json": "^2.0.0", + "tmp": "^0.2.3", + "ts-md5": "^1.3.1" + }, + "devDependencies": { + "@types/glob": "^7.1.4", + "@types/mocha": "^8.2.3", + "@types/node": "^18.11.13", + "@types/tmp": "^0.2.6", + "@types/vscode": "^1.85.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", + "glob": "^7.2.0", + "mocha": "^8.4.0", + "typescript": "^4.7.4", + "vscode-test": "^1.6.1" + }, + "engines": { + "vscode": "^1.85.0" } }, - "@babel/helper-validator-identifier": { - "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 + "node_modules/@eslint-community/eslint-utils": { + "version": "4.4.0", + "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.4.0.tgz", + "integrity": "sha512-1/sA4dwrzBAyeUoQ6oxahHKmrZvsnLCg4RfxW3ZFGGmQkSNQPFNLV9CUEFQP1x9EYXHTo5p6xdhZM1Ne9p/AfA==", + "dev": true, + "license": "MIT", + "dependencies": { + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" + } }, - "@babel/highlight": { - "version": "7.14.5", - "resolved": "https://registry.npmjs.org/@babel/highlight/-/highlight-7.14.5.tgz", - "integrity": "sha512-qf9u2WFWVV0MppaL877j2dBtQIDgmidgjGk5VIMw3OadXvYaXn66U1BFlH2t4+t3i+8PhedppRv+i40ABzd+gg==", - "dev": true, - "requires": { - "@babel/helper-validator-identifier": "^7.14.5", - "chalk": "^2.0.0", - "js-tokens": "^4.0.0" - }, - "dependencies": { - "chalk": { - "version": "2.4.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", - "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", - "dev": true, - "requires": { - "ansi-styles": "^3.2.1", - "escape-string-regexp": "^1.0.5", - "supports-color": "^5.3.0" - } - }, - "escape-string-regexp": { - "version": "1.0.5", - "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", - "integrity": "sha1-G2HAViGQqN/2rjuyzwIAyhMLhtQ=", - "dev": true - } + "node_modules/@eslint-community/regexpp": { + "version": "4.11.1", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.1.tgz", + "integrity": "sha512-m4DVN9ZqskZoLU5GlWZadwDnYo3vAEydiUayB9widCl9ffWx2IvPnp6n3on5rJmziJSw9Bv+Z3ChDVdMwXCY8Q==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, - "@eslint/eslintrc": { - "version": "0.4.3", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-0.4.3.tgz", - "integrity": "sha512-J6KFFz5QCYUJq3pf0mjEcCJVERbzv71PUIDczuh9JkwGEzced6CO5ADLHB1rbf/+oPBtoPfMYNOpGDzCANlbXw==", + "node_modules/@eslint/eslintrc": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", + "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "ajv": "^6.12.4", - "debug": "^4.1.1", - "espree": "^7.3.0", - "globals": "^13.9.0", - "ignore": "^4.0.6", + "debug": "^4.3.2", + "espree": "^9.6.0", + "globals": "^13.19.0", + "ignore": "^5.2.0", "import-fresh": "^3.2.1", - "js-yaml": "^3.13.1", - "minimatch": "^3.0.4", + "js-yaml": "^4.1.0", + "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/@eslint/eslintrc/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "@humanwhocodes/config-array": { - "version": "0.5.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.5.0.tgz", - "integrity": "sha512-FagtKFz74XrTl7y6HCzQpwDfXP0yhxe9lHLD1UZxjvZIcbyRz8zTFF/yYNfSfzU414eDwZ1SrO0Qvtyf+wFMQg==", + "node_modules/@eslint/js": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", + "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, - "requires": { - "@humanwhocodes/object-schema": "^1.2.0", - "debug": "^4.1.1", - "minimatch": "^3.0.4" + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, - "@humanwhocodes/object-schema": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-1.2.0.tgz", - "integrity": "sha512-wdppn25U8z/2yiaT6YGquE6X8sSv7hNMWSXYSSU1jGv/yd6XqjXgTDJ8KP4NgjTXfJ3GbRjeeb8RTV7a/VpM+w==", - "dev": true + "node_modules/@humanwhocodes/config-array": { + "version": "0.13.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", + "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", + "deprecated": "Use @eslint/config-array instead", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@humanwhocodes/object-schema": "^2.0.3", + "debug": "^4.3.1", + "minimatch": "^3.0.5" + }, + "engines": { + "node": ">=10.10.0" + } }, - "@nodelib/fs.scandir": { + "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, + "node_modules/@humanwhocodes/module-importer": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", + "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=12.22" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } + }, + "node_modules/@humanwhocodes/object-schema": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", + "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", + "deprecated": "Use @eslint/object-schema instead", + "dev": true, + "license": "BSD-3-Clause" + }, + "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@nodelib/fs.stat": "2.0.5", "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" } }, - "@nodelib/fs.stat": { + "node_modules/@nodelib/fs.stat": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "@nodelib/fs.walk": { + "node_modules/@nodelib/fs.walk": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" } }, - "@tootallnate/once": { + "node_modules/@tootallnate/once": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@tootallnate/once/-/once-1.1.2.tgz", "integrity": "sha512-RbzJvlNzmRq5c3O09UipeuXno4tA1FE6ikOjxZK0tuxVv3412l64l5t1W5pj4+rJq9vpkm/kwiR07aZXnsKPxw==", - "dev": true + "dev": true, + "engines": { + "node": ">= 6" + } }, - "@types/glob": { + "node_modules/@types/glob": { "version": "7.1.4", "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", "dev": true, - "requires": { + "dependencies": { "@types/minimatch": "*", "@types/node": "*" } }, - "@types/json-schema": { - "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/json-schema": { + "version": "7.0.15", + "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", + "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", + "dev": true, + "license": "MIT" }, - "@types/minimatch": { + "node_modules/@types/minimatch": { "version": "3.0.5", "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", "dev": true }, - "@types/mocha": { + "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==", "dev": true }, - "@types/node": { - "version": "14.17.18", - "resolved": "https://registry.npmjs.org/@types/node/-/node-14.17.18.tgz", - "integrity": "sha512-haYyibw4pbteEhkSg0xdDLAI3679L75EJ799ymVrPxOA922bPx3ML59SoDsQ//rHlvqpu+e36kcbR3XRQtFblA==", - "dev": true + "node_modules/@types/node": { + "version": "18.19.51", + "resolved": "https://registry.npmjs.org/@types/node/-/node-18.19.51.tgz", + "integrity": "sha512-IIMkWEIVQDlBpi6pPeGqTqOx7KbzGC3EgIyH8NrxplXOwWw0uVl9vthJUMFrxD7kcEfcRp7jIkgpB28M6JnfWA==", + "dev": true, + "license": "MIT", + "dependencies": { + "undici-types": "~5.26.4" + } }, - "@types/vscode": { - "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/@types/semver": { + "version": "7.5.8", + "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.5.8.tgz", + "integrity": "sha512-I8EUhyrgfLrcTkzV3TSsGyl1tSuPrEDzr0yd5m90UgNxQkyDXULk3b6MlQqTCpZpNtWe1K0hzclnZkTcLBe2UQ==", + "dev": true, + "license": "MIT" }, - "@typescript-eslint/eslint-plugin": { - "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==", + "node_modules/@types/tmp": { + "version": "0.2.6", + "resolved": "https://registry.npmjs.org/@types/tmp/-/tmp-0.2.6.tgz", + "integrity": "sha512-chhaNf2oKHlRkDGt+tiKE2Z5aJ6qalm7Z9rlLdBwmOiAAf09YQvvoLXjWK4HWPF1xU/fqvMgfNfpVoBscA/tKA==", "dev": true, - "requires": { - "@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", - "semver": "^7.3.5", + "license": "MIT" + }, + "node_modules/@types/vscode": { + "version": "1.93.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.93.0.tgz", + "integrity": "sha512-kUK6jAHSR5zY8ps42xuW89NLcBpw1kOabah7yv38J8MyiYuOHxLQBi0e7zeXbQgVefDy/mZZetqEFC+Fl5eIEQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/@typescript-eslint/eslint-plugin": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-5.62.0.tgz", + "integrity": "sha512-TiZzBSJja/LbhNPvk6yc0JrX9XqhQ0hdh6M2svYfsHGejaKFIAGd9MQ+ERIMzLGlN/kZoYIgdxFV0PuljTKXag==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/regexpp": "^4.4.0", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/type-utils": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", + "natural-compare-lite": "^1.4.0", + "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "@typescript-eslint/parser": "^5.0.0", + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "@typescript-eslint/experimental-utils": { - "version": "4.31.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/experimental-utils/-/experimental-utils-4.31.2.tgz", - "integrity": "sha512-3tm2T4nyA970yQ6R3JZV9l0yilE2FedYg8dcXrTar34zC9r6JB7WyBQbpIVongKPlhEMjhQ01qkwrzWy38Bk1Q==", + "node_modules/@typescript-eslint/parser": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-5.62.0.tgz", + "integrity": "sha512-VlJEV0fOQ7BExOsHYAGrgbEiZoi8D+Bl2+f6V2RrXerRSylnp+ZBHmPvaIa8cz0Ajx7WO7Z5RqfgYg7ED1nRhA==", "dev": true, - "requires": { - "@types/json-schema": "^7.0.7", - "@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" + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "debug": "^4.3.4" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "@typescript-eslint/parser": { - "version": "4.31.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-4.31.2.tgz", - "integrity": "sha512-EcdO0E7M/sv23S/rLvenHkb58l3XhuSZzKf6DBvLgHqOYdL6YFMYVtreGFWirxaU2mS1GYDby3Lyxco7X5+Vjw==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-5.62.0.tgz", + "integrity": "sha512-VXuvVvZeQCQb5Zgf4HAxc04q5j+WrNAtNh9OwCsCgpKqESMTu3tF/jhZ3xG6T4NZwWl65Bg8KuS2uEvhSfLl0w==", "dev": true, - "requires": { - "@typescript-eslint/scope-manager": "4.31.2", - "@typescript-eslint/types": "4.31.2", - "@typescript-eslint/typescript-estree": "4.31.2", - "debug": "^4.3.1" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "@typescript-eslint/scope-manager": { - "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==", + "node_modules/@typescript-eslint/type-utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-5.62.0.tgz", + "integrity": "sha512-xsSQreu+VnfbqQpW5vnCJdq1Z3Q0U31qiWmRhr98ONQmcp/yhiPJFPq8MXiJVLiksmOKSjIldZzkebzHuCGzew==", "dev": true, - "requires": { - "@typescript-eslint/types": "4.31.2", - "@typescript-eslint/visitor-keys": "4.31.2" + "license": "MIT", + "dependencies": { + "@typescript-eslint/typescript-estree": "5.62.0", + "@typescript-eslint/utils": "5.62.0", + "debug": "^4.3.4", + "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "*" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } } }, - "@typescript-eslint/types": { - "version": "4.31.2", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-4.31.2.tgz", - "integrity": "sha512-kWiTTBCTKEdBGrZKwFvOlGNcAsKGJSBc8xLvSjSppFO88AqGxGNYtF36EuEYG6XZ9vT0xX8RNiHbQUKglbSi1w==", - "dev": true + "node_modules/@typescript-eslint/types": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-5.62.0.tgz", + "integrity": "sha512-87NVngcbVXUahrRTqIK27gD2t5Cu1yuCXxbLcFtCzZGlfyVWWh8mLHkoxzjsB6DDNnvdL+fW8MiwPEJyGJQDgQ==", + "dev": true, + "license": "MIT", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + } }, - "@typescript-eslint/typescript-estree": { - "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==", + "node_modules/@typescript-eslint/typescript-estree": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-5.62.0.tgz", + "integrity": "sha512-CmcQ6uY7b9y694lKdRB8FEel7JbU/40iSAPomu++SjLMntB+2Leay2LO6i8VnJk58MtE9/nQSFIH6jpyRWyYzA==", "dev": true, - "requires": { - "@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", - "semver": "^7.3.5", + "license": "BSD-2-Clause", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/visitor-keys": "5.62.0", + "debug": "^4.3.4", + "globby": "^11.1.0", + "is-glob": "^4.0.3", + "semver": "^7.3.7", "tsutils": "^3.21.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + } + } + }, + "node_modules/@typescript-eslint/utils": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-5.62.0.tgz", + "integrity": "sha512-n8oxjeb5aIbPFEtmQxQYOLI0i9n5ySBEY/ZEHHZqKQSFnxio1rv6dthascc9dLuwrL0RC5mPCxB7vnAVGAYWAQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@types/json-schema": "^7.0.9", + "@types/semver": "^7.3.12", + "@typescript-eslint/scope-manager": "5.62.0", + "@typescript-eslint/types": "5.62.0", + "@typescript-eslint/typescript-estree": "5.62.0", + "eslint-scope": "^5.1.1", + "semver": "^7.3.7" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, - "@typescript-eslint/visitor-keys": { - "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==", + "node_modules/@typescript-eslint/visitor-keys": { + "version": "5.62.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-5.62.0.tgz", + "integrity": "sha512-07ny+LHRzQXepkGg6w0mFY41fVUNBrL2Roj/++7V1txKugfjm/Ci/qSND03r2RhlJhJYMcTn9AhhSSqQp0Ysyw==", "dev": true, - "requires": { - "@typescript-eslint/types": "4.31.2", - "eslint-visitor-keys": "^2.0.0" + "license": "MIT", + "dependencies": { + "@typescript-eslint/types": "5.62.0", + "eslint-visitor-keys": "^3.3.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" } }, - "@ungap/promise-all-settled": { + "node_modules/@ungap/promise-all-settled": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", "dev": true }, - "acorn": { - "version": "7.4.1", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-7.4.1.tgz", - "integrity": "sha512-nQyp0o1/mNdbTO1PO6kHkwSrmgZ0MT/jCCpNiwbUjGoRN4dlBhqJtoQuCnEOKzgTVwg0ZWiCoQy6SxMebQVh8A==", - "dev": true + "node_modules/@ungap/structured-clone": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", + "integrity": "sha512-zuVdFrMJiuCDQUMCzQaD6KL28MjnqqN8XnAqiEq9PNm/hCPTSGfrXCOfwj1ow4LFb/tNymJPwsNbVePc1xFqrQ==", + "dev": true, + "license": "ISC" }, - "acorn-jsx": { + "node_modules/acorn": { + "version": "8.12.1", + "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", + "integrity": "sha512-tcpGyI9zbizT9JbV6oYE477V6mTlXvvi0T0G3SNIYE2apm/G5huBa1+K89VGeovbg+jycCrfhl3ADxErOuO6Jg==", + "dev": true, + "license": "MIT", + "bin": { + "acorn": "bin/acorn" + }, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", - "dev": true + "dev": true, + "license": "MIT", + "peerDependencies": { + "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" + } }, - "agent-base": { + "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", "integrity": "sha512-RZNwNclF7+MS/8bDg70amg32dyeZGZxiDuQmZxKLAlQjr3jGyLx+4Kkk58UO7D2QdgFIQCovuSuZESne6RG6XQ==", "dev": true, - "requires": { + "dependencies": { "debug": "4" + }, + "engines": { + "node": ">= 6.0.0" } }, - "ajv": { + "node_modules/ajv": { "version": "6.12.6", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" } }, - "ansi-colors": { + "node_modules/ansi-colors": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "ansi-regex": { + "node_modules/ansi-regex": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", - "dev": true - }, - "ansi-styles": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", - "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, - "requires": { - "color-convert": "^1.9.0" + "engines": { + "node": ">=8" } }, - "anymatch": { + "node_modules/anymatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.2.tgz", "integrity": "sha512-P43ePfOAIupkguHUycrc4qJ9kz8ZiuOUijaETwX7THt0Y/GNK7v0aa8rY816xWjZ7rJdA5XdMcpVFTKMq+RvWg==", "dev": true, - "requires": { + "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" + }, + "engines": { + "node": ">= 8" } }, - "argparse": { - "version": "1.0.10", - "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", - "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", + "node_modules/argparse": { + "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, - "requires": { - "sprintf-js": "~1.0.2" - } + "license": "Python-2.0" }, - "array-union": { + "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", - "dev": true - }, - "astral-regex": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", - "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "balanced-match": { + "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, - "big-integer": { + "node_modules/big-integer": { "version": "1.6.49", "resolved": "https://registry.npmjs.org/big-integer/-/big-integer-1.6.49.tgz", "integrity": "sha512-KJ7VhqH+f/BOt9a3yMwJNmcZjG53ijWMTjSAGMveQWyLwqIiwkjNP5PFgDob3Snnx86SjDj6I89fIbv0dkQeNw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.6" + } }, - "binary": { + "node_modules/binary": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/binary/-/binary-0.3.0.tgz", "integrity": "sha1-n2BVO8XOjDOG87VTz/R0Yq3sqnk=", "dev": true, - "requires": { + "dependencies": { "buffers": "~0.1.1", "chainsaw": "~0.1.0" + }, + "engines": { + "node": "*" } }, - "binary-extensions": { + "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==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "bluebird": { + "node_modules/bluebird": { "version": "3.4.7", "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.4.7.tgz", "integrity": "sha1-9y12C+Cbf3bQjtj66Ysomo0F+rM=", "dev": true }, - "brace-expansion": { + "node_modules/brace-expansion": { "version": "1.1.11", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, - "requires": { + "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, - "braces": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz", - "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==", + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", "dev": true, - "requires": { - "fill-range": "^7.0.1" + "license": "MIT", + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" } }, - "browser-stdout": { + "node_modules/browser-stdout": { "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 }, - "buffer-indexof-polyfill": { + "node_modules/buffer-indexof-polyfill": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/buffer-indexof-polyfill/-/buffer-indexof-polyfill-1.0.2.tgz", "integrity": "sha512-I7wzHwA3t1/lwXQh+A5PbNvJxgfo5r3xulgpYDB5zckTu/Z9oUK9biouBKQUjEqzaz3HnAT6TYoovmE+GqSf7A==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10" + } }, - "buffers": { + "node_modules/buffers": { "version": "0.1.1", "resolved": "https://registry.npmjs.org/buffers/-/buffers-0.1.1.tgz", "integrity": "sha1-skV5w77U1tOWru5tmorn9Ugqt7s=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.2.0" + } }, - "callsites": { + "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "camelcase": { + "node_modules/camelcase": { "version": "6.2.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.2.0.tgz", "integrity": "sha512-c7wVvbw3f37nuobQNtgsgG9POC9qMbNuMQmTCqZv23b6MIz0fcYpBiOlv9gEN/hdLdnZTDQhg6e9Dq5M1vKvfg==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "chainsaw": { + "node_modules/chainsaw": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/chainsaw/-/chainsaw-0.1.0.tgz", "integrity": "sha1-XqtQsor+WAdNDVgpE4iCi15fvJg=", "dev": true, - "requires": { + "dependencies": { "traverse": ">=0.3.0 <0.4" + }, + "engines": { + "node": "*" } }, - "chalk": { + "node_modules/chalk": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.1.0", "supports-color": "^7.1.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/chalk/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "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 - }, - "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" - } - } + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/chalk/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/chalk/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/chalk/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, + "engines": { + "node": ">=8" + } + }, + "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, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "chokidar": { + "node_modules/chokidar": { "version": "3.5.1", "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", "dev": true, - "requires": { + "dependencies": { "anymatch": "~3.1.1", "braces": "~3.0.2", - "fsevents": "~2.3.1", "glob-parent": "~5.1.0", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", "readdirp": "~3.5.0" + }, + "engines": { + "node": ">= 8.10.0" + }, + "optionalDependencies": { + "fsevents": "~2.3.1" } }, - "cliui": { + "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==", "dev": true, - "requires": { + "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.0", "wrap-ansi": "^7.0.0" } }, - "color-convert": { - "version": "1.9.3", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", - "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", - "dev": true, - "requires": { - "color-name": "1.1.3" - } - }, - "color-name": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", - "integrity": "sha1-p9BVi9icQveV3UIyj3QIMcpTvCU=", - "dev": true - }, - "concat-map": { + "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha1-2Klr13/Wjfd5OnMDajug1UBdR3s=", "dev": true }, - "core-util-is": { + "node_modules/core-util-is": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/core-util-is/-/core-util-is-1.0.3.tgz", "integrity": "sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==", "dev": true }, - "cross-spawn": { + "node_modules/cross-spawn": { "version": "7.0.3", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.3.tgz", "integrity": "sha512-iRDPJKUPVEND7dHPO8rkbOnPpyDygcDFtWjpeWNCgy8WP2rXcxXL8TskReQl6OrB2G7+UJrags1q15Fudc7G6w==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" + }, + "engines": { + "node": ">= 8" } }, - "debug": { - "version": "4.3.2", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.2.tgz", - "integrity": "sha512-mOp8wKcvj7XxC78zLgw/ZA+6TSgkoE2C/ienthhRD298T7UNwAg9diBpLRxC0mOezLl4B0xV7M0cCO6P/O0Xhw==", + "node_modules/debug": { + "version": "4.3.7", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.7.tgz", + "integrity": "sha512-Er2nc/H7RrMXZBFCEim6TCmMk02Z8vLC2Rbi1KEBggpo0fS6l0S1nnapwmIi3yW/+GOJap1Krg4w0Hg80oCqgQ==", "dev": true, - "requires": { - "ms": "2.1.2" + "license": "MIT", + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } } }, - "decamelize": { + "node_modules/decamelize": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/decamelize/-/decamelize-4.0.0.tgz", "integrity": "sha512-9iE1PgSik9HeIIw2JO94IidnE3eBoQrFJ3w7sFuzSX4DpmZ3v5sZpUiV5Swcf6mQEF+Y0ru8Neo+p+nyh2J+hQ==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "deep-is": { + "node_modules/deep-is": { "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 + "dev": true, + "license": "MIT" }, - "diff": { + "node_modules/diff": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.3.1" + } }, - "dir-glob": { + "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "path-type": "^4.0.0" + }, + "engines": { + "node": ">=8" } }, - "doctrine": { + "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, - "requires": { + "license": "Apache-2.0", + "dependencies": { "esutils": "^2.0.2" + }, + "engines": { + "node": ">=6.0.0" } }, - "duplexer2": { + "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", "integrity": "sha1-ixLauHjA1p4+eJEFFmKjL8a93ME=", "dev": true, - "requires": { + "dependencies": { "readable-stream": "^2.0.2" } }, - "emoji-regex": { + "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", "dev": true }, - "enquirer": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/enquirer/-/enquirer-2.3.6.tgz", - "integrity": "sha512-yjNnPr315/FjS4zIsUxYguYUPP2e1NK4d7E7ZOLiyYCcbFBiTMyID+2wvm2w6+pZ/odMA7cRkjhsPbltwBOrLg==", - "dev": true, - "requires": { - "ansi-colors": "^4.1.1" - } - }, - "escalade": { + "node_modules/escalade": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.1.1.tgz", "integrity": "sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==", - "dev": true + "dev": true, + "engines": { + "node": ">=6" + } }, - "escape-string-regexp": { + "node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "eslint": { - "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==", + "node_modules/eslint": { + "version": "8.57.1", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", + "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "dev": true, - "requires": { - "@babel/code-frame": "7.12.11", - "@eslint/eslintrc": "^0.4.3", - "@humanwhocodes/config-array": "^0.5.0", - "ajv": "^6.10.0", + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.2.0", + "@eslint-community/regexpp": "^4.6.1", + "@eslint/eslintrc": "^2.1.4", + "@eslint/js": "8.57.1", + "@humanwhocodes/config-array": "^0.13.0", + "@humanwhocodes/module-importer": "^1.0.1", + "@nodelib/fs.walk": "^1.2.8", + "@ungap/structured-clone": "^1.2.0", + "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", - "debug": "^4.0.1", + "debug": "^4.3.2", "doctrine": "^3.0.0", - "enquirer": "^2.3.5", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^5.1.1", - "eslint-utils": "^2.1.0", - "eslint-visitor-keys": "^2.0.0", - "espree": "^7.3.1", - "esquery": "^1.4.0", + "eslint-scope": "^7.2.2", + "eslint-visitor-keys": "^3.4.3", + "espree": "^9.6.1", + "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", - "functional-red-black-tree": "^1.0.1", - "glob-parent": "^5.1.2", - "globals": "^13.6.0", - "ignore": "^4.0.6", - "import-fresh": "^3.0.0", + "find-up": "^5.0.0", + "glob-parent": "^6.0.2", + "globals": "^13.19.0", + "graphemer": "^1.4.0", + "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", - "js-yaml": "^3.13.1", + "is-path-inside": "^3.0.3", + "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", - "minimatch": "^3.0.4", + "minimatch": "^3.1.2", "natural-compare": "^1.4.0", - "optionator": "^0.9.1", - "progress": "^2.0.0", - "regexpp": "^3.1.0", - "semver": "^7.2.1", - "strip-ansi": "^6.0.0", - "strip-json-comments": "^3.1.0", - "table": "^6.0.9", - "text-table": "^0.2.0", - "v8-compile-cache": "^2.0.3" - }, - "dependencies": { - "eslint-utils": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-2.1.0.tgz", - "integrity": "sha512-w94dQYoauyvlDc43XnGB8lU3Zt713vNChgt4EWwhXAP2XkBvndfxF0AgIqKOOasjPIPzj9JqgwkwbCYD0/V3Zg==", - "dev": true, - "requires": { - "eslint-visitor-keys": "^1.1.0" - }, - "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } - } - }, - "ignore": { - "version": "4.0.6", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-4.0.6.tgz", - "integrity": "sha512-cyFDKrqc/YdcWFniJhzI42+AzS+gNwmUzOSFcRCQYwySuBBBy/KjuxWLZ/FHEH6Moq1NizMOBWyTcv8O4OZIMg==", - "dev": true - } + "optionator": "^0.9.3", + "strip-ansi": "^6.0.1", + "text-table": "^0.2.0" + }, + "bin": { + "eslint": "bin/eslint.js" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-scope": { + "node_modules/eslint-scope": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-5.1.1.tgz", "integrity": "sha512-2NxwbF/hZ0KpepYN0cNbo+FN6XoK7GaHlQhgx/hIZl6Va0bF45RQOOwhLIy8lQDbuCiadSLCBnH2CFYquit5bw==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^4.1.1" + }, + "engines": { + "node": ">=8.0.0" } }, - "eslint-utils": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/eslint-utils/-/eslint-utils-3.0.0.tgz", - "integrity": "sha512-uuQC43IGctw68pJA1RgbQS8/NP7rch6Cwd4j3ZBtgo4/8Flj4eGE7ZYSZRN3iq5pVUv6GPdW5Z1RFleo84uLDA==", + "node_modules/eslint-visitor-keys": { + "version": "3.4.3", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", + "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, - "requires": { - "eslint-visitor-keys": "^2.0.0" + "license": "Apache-2.0", + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" } }, - "eslint-visitor-keys": { - "version": "2.1.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-2.1.0.tgz", - "integrity": "sha512-0rSmRBzXgDzIsD6mGdJgevzgezI534Cer5L/vyMX0kHzT/jiB43jRhd9YUlMGYLQy2zprNmoT8qasCGtY+QaKw==", - "dev": true + "node_modules/eslint/node_modules/eslint-scope": { + "version": "7.2.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", + "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "esrecurse": "^4.3.0", + "estraverse": "^5.2.0" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "espree": { - "version": "7.3.1", - "resolved": "https://registry.npmjs.org/espree/-/espree-7.3.1.tgz", - "integrity": "sha512-v3JCNCE64umkFpmkFGqzVKsOT0tN1Zr+ueqLZfpV1Ob8e+CEgPWa+OxCoGH3tnhimMKIaBm4m/vaRpJ/krRz2g==", + "node_modules/eslint/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, - "requires": { - "acorn": "^7.4.0", - "acorn-jsx": "^5.3.1", - "eslint-visitor-keys": "^1.3.0" + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/eslint/node_modules/glob-parent": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", + "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", + "dev": true, + "license": "ISC", + "dependencies": { + "is-glob": "^4.0.3" }, + "engines": { + "node": ">=10.13.0" + } + }, + "node_modules/eslint/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "license": "ISC", "dependencies": { - "eslint-visitor-keys": { - "version": "1.3.0", - "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-1.3.0.tgz", - "integrity": "sha512-6J72N8UNa462wa/KFODt/PJ3IU60SDpC3QXC1Hjc1BXXpfL2C9R5+AU7jhe0F6GREqVMh4Juu+NY7xn+6dipUQ==", - "dev": true - } + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "esprima": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", - "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", - "dev": true + "node_modules/espree": { + "version": "9.6.1", + "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", + "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", + "dev": true, + "license": "BSD-2-Clause", + "dependencies": { + "acorn": "^8.9.0", + "acorn-jsx": "^5.3.2", + "eslint-visitor-keys": "^3.4.1" + }, + "engines": { + "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } }, - "esquery": { - "version": "1.4.0", - "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.4.0.tgz", - "integrity": "sha512-cCDispWt5vHHtwMY2YrAQ4ibFkAL8RbH5YGBnZBc90MolvvfkkQcJro/aZiAQUlQ3qgrYS6D6v8Gc5G5CQsc9w==", + "node_modules/esquery": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.6.0.tgz", + "integrity": "sha512-ca9pw9fomFcKPvFLXhBKUK90ZvGibiGOvRJNbjljY7s7uq/5YO4BOzcYtJqExdx99rF6aAcnRxHmcUHcz6sQsg==", "dev": true, - "requires": { + "license": "BSD-3-Clause", + "dependencies": { "estraverse": "^5.1.0" }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "engines": { + "node": ">=0.10" + } + }, + "node_modules/esquery/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" } }, - "esrecurse": { + "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "estraverse": "^5.2.0" }, - "dependencies": { - "estraverse": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.2.0.tgz", - "integrity": "sha512-BxbNGGNm0RyRYvUdHpIwv9IWzeM9XClbOxwoATuFdOE7ZE6wHL+HQ5T8hoPM+zHvmKzzsEqhgy0GrQ5X13afiQ==", - "dev": true - } + "engines": { + "node": ">=4.0" } }, - "estraverse": { + "node_modules/esrecurse/node_modules/estraverse": { + "version": "5.3.0", + "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", + "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } + }, + "node_modules/estraverse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-4.3.0.tgz", "integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==", - "dev": true + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=4.0" + } }, - "esutils": { + "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", - "dev": true + "dev": true, + "license": "BSD-2-Clause", + "engines": { + "node": ">=0.10.0" + } }, - "fast-deep-equal": { + "node_modules/fast-deep-equal": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-glob": { - "version": "3.2.7", - "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.2.7.tgz", - "integrity": "sha512-rYGMRwip6lUMvYD3BTScMwT1HtAs2d71SMv66Vrxs0IekGZEjhM0pcMfjQPnknBt2zeCwQMEupiN02ZP4DiT1Q==", + "node_modules/fast-glob": { + "version": "3.3.2", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.2.tgz", + "integrity": "sha512-oX2ruAFQwf/Orj8m737Y5adxDQO0LAB7/S5MnxCdTNDd4p6BsyIVsv9JQsATbTSq8KHRpLwIHbVlUNatxd+1Ow==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.4" + }, + "engines": { + "node": ">=8.6.0" } }, - "fast-json-stable-stringify": { + "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", - "dev": true + "dev": true, + "license": "MIT" }, - "fast-levenshtein": { + "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", - "integrity": "sha1-PYpcZog6FqMMqGQ+hR8Zuqd5eRc=", - "dev": true + "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", + "dev": true, + "license": "MIT" }, - "fastq": { - "version": "1.13.0", - "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.13.0.tgz", - "integrity": "sha512-YpkpUnK8od0o1hmeSc7UUs/eB/vIPWJYjKck2QKIzAf71Vm1AAQ3EbuZB3g2JIy+pg+ERD0vqI79KyZiB2e2Nw==", + "node_modules/fastq": { + "version": "1.17.1", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.17.1.tgz", + "integrity": "sha512-sRVD3lWVIXWg6By68ZN7vho9a1pQcN/WBFaAAsDDFzlJjvoGx0P8z7V1t72grFJfJhu3YPZBuu25f7Kaw2jN1w==", "dev": true, - "requires": { + "license": "ISC", + "dependencies": { "reusify": "^1.0.4" } }, - "file-entry-cache": { + "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "flat-cache": "^3.0.4" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "fill-range": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz", - "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==", + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "find-up": { + "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, - "requires": { + "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "flat": { + "node_modules/flat": { "version": "5.0.2", "resolved": "https://registry.npmjs.org/flat/-/flat-5.0.2.tgz", "integrity": "sha512-b6suED+5/3rTpUBdG1gupIl8MPFCAMA0QXwmljLhvCUKcUvdE4gWky9zpuGCcXHOsz4J9wPGNWq6OKpmIzz3hQ==", - "dev": true + "dev": true, + "bin": { + "flat": "cli.js" + } }, - "flat-cache": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.0.4.tgz", - "integrity": "sha512-dm9s5Pw7Jc0GvMYbshN6zchCA9RgQlzzEZX3vylR9IqFfS8XciblUXOKfW6SiuJ0e13eDYZoZV5wdrev7P3Nwg==", + "node_modules/flat-cache": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", + "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, - "requires": { - "flatted": "^3.1.0", + "license": "MIT", + "dependencies": { + "flatted": "^3.2.9", + "keyv": "^4.5.3", "rimraf": "^3.0.2" + }, + "engines": { + "node": "^10.12.0 || >=12.0.0" } }, - "flatted": { - "version": "3.2.2", - "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.2.2.tgz", - "integrity": "sha512-JaTY/wtrcSyvXJl4IMFHPKyFur1sE9AUqc0QnhOaJ0CxHtAoIV8pYDzeEfAaNEtGkOfq4gr3LBFmdXW5mOQFnA==", - "dev": true + "node_modules/flatted": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.1.tgz", + "integrity": "sha512-X8cqMLLie7KsNUDSdzeN8FYK9rEt4Dt67OsG/DNGnYTSDBG4uFAJFBnUeiV+zCVAvwFy56IjM9sH51jVaEhNxw==", + "dev": true, + "license": "ISC" }, - "fs.realpath": { + "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha1-FQStJSMVjKpA20onh8sBQRmU6k8=", "dev": true }, - "fsevents": { + "node_modules/fsevents": { "version": "2.3.2", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.2.tgz", "integrity": "sha512-xiqMQR4xAeHTuB9uWm+fFRcIOgKBMiOBP+eXiyT7jsgVCq1bkVygt00oASowB7EdtpOHaaPgKt812P9ab+DDKA==", "dev": true, - "optional": true + "hasInstallScript": true, + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": "^8.16.0 || ^10.6.0 || >=11.0.0" + } }, - "fstream": { + "node_modules/fstream": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/fstream/-/fstream-1.0.12.tgz", "integrity": "sha512-WvJ193OHa0GHPEL+AycEJgxvBEwyfRkN1vhjca23OaPVMCaLCXTd5qAu82AjTcgP1UJmytkOKb63Ypde7raDIg==", + "deprecated": "This package is no longer supported.", "dev": true, - "requires": { + "dependencies": { "graceful-fs": "^4.1.2", "inherits": "~2.0.0", "mkdirp": ">=0.5 0", "rimraf": "2" }, - "dependencies": { - "rimraf": { - "version": "2.7.1", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", - "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", - "dev": true, - "requires": { - "glob": "^7.1.3" - } - } + "engines": { + "node": ">=0.6" } }, - "functional-red-black-tree": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/functional-red-black-tree/-/functional-red-black-tree-1.0.1.tgz", - "integrity": "sha1-GwqzvVU7Kg1jmdKcDj6gslIHgyc=", - "dev": true + "node_modules/fstream/node_modules/rimraf": { + "version": "2.7.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", + "integrity": "sha512-uWjbaKIK3T1OSVptzX7Nl6PvQ3qAGtKEtVRjRuazjfL3Bx5eI409VZSqgND+4UNnmzLVdPj9FqFJNPqBZFve4w==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", + "dev": true, + "dependencies": { + "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + } }, - "get-caller-file": { + "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", - "dev": true + "dev": true, + "engines": { + "node": "6.* || 8.* || >= 10.*" + } }, - "glob": { + "node_modules/glob": { "version": "7.2.0", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, - "requires": { + "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.0.4", "once": "^1.3.0", "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "glob-parent": { + "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, - "requires": { + "dependencies": { "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" } }, - "globals": { - "version": "13.11.0", - "resolved": "https://registry.npmjs.org/globals/-/globals-13.11.0.tgz", - "integrity": "sha512-08/xrJ7wQjK9kkkRoI3OFUBbLx4f+6x3SGwcPvQ0QH6goFDrOU2oyAWrmh3dJezu65buo+HBMzAMQy6rovVC3g==", + "node_modules/globals": { + "version": "13.24.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", + "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "type-fest": "^0.20.2" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "globby": { - "version": "11.0.4", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.0.4.tgz", - "integrity": "sha512-9O4MVG9ioZJ08ffbcyVYyLOJLk5JQ688pJ4eMGLpdWLHq/Wr1D9BlriLQyL0E+jbkuePVZXYFj47QM/v093wHg==", + "node_modules/globby": { + "version": "11.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", + "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", - "fast-glob": "^3.1.1", - "ignore": "^5.1.4", - "merge2": "^1.3.0", + "fast-glob": "^3.2.9", + "ignore": "^5.2.0", + "merge2": "^1.4.1", "slash": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "graceful-fs": { + "node_modules/graceful-fs": { "version": "4.2.8", "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.8.tgz", "integrity": "sha512-qkIilPUYcNhJpd33n0GBXTB1MMPp14TxEsEs0pTrsSVucApsYzW5V+Q8Qxhik6KU3evy+qkAAowTByymK0avdg==", "dev": true }, - "growl": { + "node_modules/graphemer": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", + "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", + "dev": true, + "license": "MIT" + }, + "node_modules/growl": { "version": "1.10.5", "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true - }, - "has-flag": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", - "integrity": "sha1-tdRU3CGZriJWmfNGfloH87lVuv0=", - "dev": true + "dev": true, + "engines": { + "node": ">=4.x" + } }, - "he": { + "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", "integrity": "sha512-F/1DnUGPopORZi0ni+CvrCgHQ5FyEAHRLSApuYWMmrbSwoN2Mn/7k+Gl38gJnR7yyDZk6WLXwiGod1JOWNDKGw==", - "dev": true + "dev": true, + "bin": { + "he": "bin/he" + } }, - "http-proxy-agent": { + "node_modules/http-proxy-agent": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-4.0.1.tgz", "integrity": "sha512-k0zdNgqWTGA6aeIRVpvfVob4fL52dTfaehylg0Y4UvSySvOq/Y+BOyPrgpUrA7HylqvU8vIZGsRuXmspskV0Tg==", "dev": true, - "requires": { + "dependencies": { "@tootallnate/once": "1", "agent-base": "6", "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "https-proxy-agent": { + "node_modules/https-proxy-agent": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-5.0.0.tgz", "integrity": "sha512-EkYm5BcKUGiduxzSt3Eppko+PiNWNEpa4ySk9vTC6wDsQJW9rHSa+UhGNJoRYp7bz6Ht1eaRIa6QaJqO5rCFbA==", "dev": true, - "requires": { + "dependencies": { "agent-base": "6", "debug": "4" + }, + "engines": { + "node": ">= 6" } }, - "ignore": { - "version": "5.1.8", - "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.1.8.tgz", - "integrity": "sha512-BMpfD7PpiETpBl/A6S498BaIJ6Y/ABT93ETbby2fP00v4EbvPBXWEoaR1UBPKs3iR53pJY7EtZk5KACI57i1Uw==", - "dev": true + "node_modules/ignore": { + "version": "5.3.2", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", + "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 4" + } }, - "import-fresh": { + "node_modules/import-fresh": { "version": "3.3.0", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" + }, + "engines": { + "node": ">=6" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "imurmurhash": { + "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", - "integrity": "sha1-khi5srkoojixPcT7a21XbyMUU+o=", - "dev": true + "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.8.19" + } }, - "inflight": { + "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha1-Sb1jMdfQLQwJvJEKEHW6gWW1bfk=", + "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, - "requires": { + "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, - "inherits": { + "node_modules/inherits": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", "dev": true }, - "is-binary-path": { + "node_modules/is-binary-path": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-binary-path/-/is-binary-path-2.1.0.tgz", "integrity": "sha512-ZMERYes6pDydyuGidse7OsHxtbI7WVeUEozgR/g7rd0xUimYNlvZRE/K2MgZTjWy725IfelLeVcEM97mmtRGXw==", "dev": true, - "requires": { + "dependencies": { "binary-extensions": "^2.0.0" + }, + "engines": { + "node": ">=8" } }, - "is-extglob": { + "node_modules/is-extglob": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", "integrity": "sha1-qIwCU1eR8C7TfHahueqXc8gz+MI=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "is-fullwidth-code-point": { + "node_modules/is-fullwidth-code-point": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "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==", + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" } }, - "is-number": { + "node_modules/is-number": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "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" + } }, - "is-plain-obj": { + "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 + "dev": true, + "engines": { + "node": ">=8" + } }, - "isarray": { + "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", "integrity": "sha1-u5NdSFgsuhaMBoNJV6VKPgcSTxE=", "dev": true }, - "isexe": { + "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, - "js-tokens": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", - "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", - "dev": true - }, - "js-yaml": { - "version": "3.14.1", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", - "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", + "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==", "dev": true, - "requires": { - "argparse": "^1.0.7", - "esprima": "^4.0.0" + "license": "MIT", + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" } }, - "json-schema-traverse": { + "node_modules/json-buffer": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", + "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", + "dev": true, + "license": "MIT" + }, + "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", - "dev": true + "dev": true, + "license": "MIT" }, - "json-stable-stringify-without-jsonify": { + "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", - "integrity": "sha1-nbe1lJatPzz+8wp1FC0tkwrXJlE=", - "dev": true + "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", + "dev": true, + "license": "MIT" }, - "levn": { + "node_modules/keyv": { + "version": "4.5.4", + "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", + "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", + "dev": true, + "license": "MIT", + "dependencies": { + "json-buffer": "3.0.1" + } + }, + "node_modules/leader-line": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/leader-line/-/leader-line-1.0.7.tgz", + "integrity": "sha512-PhJpQLV7XychSIuGPD0TEqR9PgRYBbrhReaOcmHFOVtTw4dWKfUMAtOb7UP7xTsoib6sFzq2giQeOUHy7LCuJQ==", + "license": "MIT" + }, + "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" + }, + "engines": { + "node": ">= 0.8.0" } }, - "listenercount": { + "node_modules/listenercount": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/listenercount/-/listenercount-1.0.1.tgz", "integrity": "sha1-hMinKrWcRyUyFIDJdeZQg0LnCTc=", "dev": true }, - "locate-path": { + "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, - "requires": { + "dependencies": { "p-locate": "^5.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "lodash.clonedeep": { - "version": "4.5.0", - "resolved": "https://registry.npmjs.org/lodash.clonedeep/-/lodash.clonedeep-4.5.0.tgz", - "integrity": "sha1-4j8/nE+Pvd6HJSnBBxhXoIblzO8=", - "dev": true - }, - "lodash.merge": { + "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", - "dev": true - }, - "lodash.truncate": { - "version": "4.4.2", - "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", - "integrity": "sha1-WjUNoLERO4N+z//VgSy+WNbq4ZM=", - "dev": true + "dev": true, + "license": "MIT" }, - "log-symbols": { + "node_modules/log-symbols": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", "dev": true, - "requires": { + "dependencies": { "chalk": "^4.0.0" + }, + "engines": { + "node": ">=10" } }, - "lru-cache": { - "version": "6.0.0", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", - "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", - "dev": true, - "requires": { - "yallist": "^4.0.0" - } + "node_modules/map2object": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/map2object/-/map2object-1.0.0.tgz", + "integrity": "sha512-JpaZKIuEcjxjcyLSykhirzd706MIVa6agPbidZbNCifl+6MHdFZ7U0Bg8cno3PN0Z9a1A690OsvxMSjc/gc9gg==", + "license": "MIT" }, - "merge2": { + "node_modules/merge2": { "version": "1.4.1", "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 8" + } }, - "micromatch": { - "version": "4.0.4", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.4.tgz", - "integrity": "sha512-pRmzw/XUcwXGpD9aI9q/0XOwLNygjETJ8y0ao0wdqprrzDa4YnxLcz7fQRZr8voh8V10kGhABbNcHVk5wHgWwg==", + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, - "requires": { - "braces": "^3.0.1", - "picomatch": "^2.2.3" + "license": "MIT", + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" } }, - "minimatch": { + "node_modules/minimatch": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", "dev": true, - "requires": { + "dependencies": { "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" } }, - "minimist": { + "node_modules/minimist": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", "dev": true }, - "mkdirp": { + "node_modules/mkdirp": { "version": "0.5.5", "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-0.5.5.tgz", "integrity": "sha512-NKmAlESf6jMGym1++R0Ra7wvhV+wFW63FaSOFPwRahvea0gMUcGUhVeAg/0BC0wiv9ih5NYPB1Wn1UEI1/L+xQ==", "dev": true, - "requires": { + "dependencies": { "minimist": "^1.2.5" + }, + "bin": { + "mkdirp": "bin/cmd.js" } }, - "mocha": { + "node_modules/mocha": { "version": "8.4.0", "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", "dev": true, - "requires": { + "dependencies": { "@ungap/promise-all-settled": "1.1.2", "ansi-colors": "4.1.1", "browser-stdout": "1.3.1", @@ -1276,225 +1795,323 @@ "yargs-parser": "20.2.4", "yargs-unparser": "2.0.0" }, + "bin": { + "_mocha": "bin/_mocha", + "mocha": "bin/mocha" + }, + "engines": { + "node": ">= 10.12.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/mochajs" + } + }, + "node_modules/mocha/node_modules/debug": { + "version": "4.3.1", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", + "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", + "dev": true, + "dependencies": { + "ms": "2.1.2" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/mocha/node_modules/debug/node_modules/ms": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", + "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", + "dev": true + }, + "node_modules/mocha/node_modules/glob": { + "version": "7.1.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", + "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.0.4", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/mocha/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, + "engines": { + "node": ">=8" + } + }, + "node_modules/mocha/node_modules/js-yaml": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", + "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", + "dev": true, + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/mocha/node_modules/supports-color": { + "version": "8.1.1", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", + "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", + "dev": true, "dependencies": { - "argparse": { - "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 - }, - "debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "requires": { - "ms": "2.1.2" - }, - "dependencies": { - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - } - } - }, - "glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", - "dev": true, - "requires": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - } - }, - "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 - }, - "js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "dev": true, - "requires": { - "argparse": "^2.0.1" - } - }, - "ms": { - "version": "2.1.3", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", - "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", - "dev": true - }, - "supports-color": { - "version": "8.1.1", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", - "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", - "dev": true, - "requires": { - "has-flag": "^4.0.0" - } - } + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/supports-color?sponsor=1" } }, - "ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true, + "license": "MIT" }, - "nanoid": { + "node_modules/nanoid": { "version": "3.1.20", "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "dev": true + "dev": true, + "bin": { + "nanoid": "bin/nanoid.cjs" + }, + "engines": { + "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" + } }, - "natural-compare": { + "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", - "integrity": "sha1-Sr6/7tdUHywnrPspvbvRXI1bpPc=", - "dev": true + "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", + "dev": true, + "license": "MIT" }, - "normalize-path": { + "node_modules/natural-compare-lite": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/natural-compare-lite/-/natural-compare-lite-1.4.0.tgz", + "integrity": "sha512-Tj+HTDSJJKaZnfiuw+iaF9skdPpTo2GtEly5JHnWV/hfv2Qj/9RKsGISQtLh2ox3l5EAGw487hnBee0sIJ6v2g==", + "dev": true, + "license": "MIT" + }, + "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "once": { + "node_modules/once": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", "integrity": "sha1-WDsap3WWHUsROsF9nFC6753Xa9E=", "dev": true, - "requires": { + "dependencies": { "wrappy": "1" } }, - "optionator": { - "version": "0.9.1", - "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.1.tgz", - "integrity": "sha512-74RlY5FCnhq4jRxVUPKDaRwrVNXMqsGsiW6AJw4XK8hmtm10wC0ypZBLw5IIp85NZMr91+qd1RvvENwg7jjRFw==", + "node_modules/optionator": { + "version": "0.9.4", + "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", + "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", - "word-wrap": "^1.2.3" + "word-wrap": "^1.2.5" + }, + "engines": { + "node": ">= 0.8.0" } }, - "p-limit": { + "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, - "requires": { + "dependencies": { "yocto-queue": "^0.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "p-locate": { + "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, - "requires": { + "dependencies": { "p-limit": "^3.0.2" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "parent-module": { + "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "callsites": "^3.0.0" + }, + "engines": { + "node": ">=6" } }, - "path-exists": { + "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", - "dev": true + "dev": true, + "engines": { + "node": ">=8" + } }, - "path-is-absolute": { + "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha1-F0uSaHNVNP+8es5r9TpanhtcX18=", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "path-key": { + "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "path-type": { + "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "picomatch": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.0.tgz", - "integrity": "sha512-lY1Q/PiJGC2zOv/z391WOTD+Z02bCgsFfvxoXXf6h7kv9o+WmsmzYqrAwY63sNgOxE4xEdq0WyUnXfKeBrSvYw==", - "dev": true + "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" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } }, - "prelude-ls": { + "node_modules/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">= 0.8.0" + } }, - "process-nextick-args": { + "node_modules/process-nextick-args": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/process-nextick-args/-/process-nextick-args-2.0.1.tgz", "integrity": "sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==", "dev": true }, - "progress": { - "version": "2.0.3", - "resolved": "https://registry.npmjs.org/progress/-/progress-2.0.3.tgz", - "integrity": "sha512-7PiHtLll5LdnKIMw100I+8xJXR5gW2QwWYkT6iJva0bXitZKa/XMrSbdmg3r2Xnaidz9Qumd0VPaMrZlF9V9sA==", - "dev": true - }, - "punycode": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.1.1.tgz", - "integrity": "sha512-XRsRjdf+j5ml+y/6GKHPZbrF/8p2Yga0JPtdqTIY2Xe5ohJPD9saDJJLPvp9+NSBprVvevdXZybnj2cv8OEd0A==", - "dev": true + "node_modules/punycode": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", + "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=6" + } }, - "queue-microtask": { + "node_modules/queue-microtask": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT" }, - "randombytes": { + "node_modules/randombytes": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/randombytes/-/randombytes-2.1.0.tgz", "integrity": "sha512-vYl3iOX+4CKUWuxGi9Ukhie6fsqXqS9FE2Zaic4tNFD2N2QQaXOMFbuKK4QmDHC0JO6B1Zp41J0LpT0oR68amQ==", "dev": true, - "requires": { + "dependencies": { "safe-buffer": "^5.1.0" } }, - "readable-stream": { + "node_modules/readable-stream": { "version": "2.3.7", "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-2.3.7.tgz", "integrity": "sha512-Ebho8K4jIbHAxnuxi7o42OrZgF/ZTNcsZj6nRKyUmkhLFq8CHItp/fy6hQZuZmP/n3yZ9VBUbp4zz/mX8hmYPw==", "dev": true, - "requires": { + "dependencies": { "core-util-is": "~1.0.0", "inherits": "~2.0.3", "isarray": "~1.0.0", @@ -1502,316 +2119,365 @@ "safe-buffer": "~5.1.1", "string_decoder": "~1.1.1", "util-deprecate": "~1.0.1" - }, - "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } } }, - "readdirp": { + "node_modules/readable-stream/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", + "dev": true + }, + "node_modules/readdirp": { "version": "3.5.0", "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", "dev": true, - "requires": { + "dependencies": { "picomatch": "^2.2.1" + }, + "engines": { + "node": ">=8.10.0" } }, - "regexpp": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/regexpp/-/regexpp-3.2.0.tgz", - "integrity": "sha512-pq2bWo9mVD43nbts2wGv17XLiNLya+GklZ8kaDLV2Z08gDCsGpnKn9BFMepvWuHCbyVvY7J5o5+BVvoQbmlJLg==", - "dev": true - }, - "require-directory": { + "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha1-jGStX9MNqxyXbiNE/+f3kqam30I=", - "dev": true - }, - "require-from-string": { - "version": "2.0.2", - "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", - "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", - "dev": true + "dev": true, + "engines": { + "node": ">=0.10.0" + } }, - "resolve-from": { + "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=4" + } }, - "reusify": { + "node_modules/reusify": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.0.4.tgz", "integrity": "sha512-U9nH88a3fc/ekCF1l0/UP1IosiuIjyTh7hBvXVMHYgVcfGvt897Xguj2UOLDeI5BG2m7/uwyaLVT6fbtCwTyzw==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } }, - "rimraf": { + "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", + "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, - "requires": { + "dependencies": { "glob": "^7.1.3" + }, + "bin": { + "rimraf": "bin.js" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" } }, - "run-parallel": { + "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", "dev": true, - "requires": { + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "license": "MIT", + "dependencies": { "queue-microtask": "^1.2.2" } }, - "safe-buffer": { + "node_modules/safe-buffer": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", - "dev": true + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] }, - "semver": { - "version": "7.3.5", - "resolved": "https://registry.npmjs.org/semver/-/semver-7.3.5.tgz", - "integrity": "sha512-PoeGJYh8HK4BTO/a9Tf6ZG3veo/A7ZVsYrSA6J8ny9nb3B1VrpkuN+z9OE5wfE5p6H4LchYZsegiQgbJD94ZFQ==", + "node_modules/semver": { + "version": "7.6.3", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.6.3.tgz", + "integrity": "sha512-oVekP1cKtI+CTDvHWYFUcMtsK/00wmAEfyqKfNdARm8u1wNVhSgaX7A8d4UuIlUI5e84iEwOhs7ZPYRmzU9U6A==", "dev": true, - "requires": { - "lru-cache": "^6.0.0" + "license": "ISC", + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" } }, - "serialize-javascript": { + "node_modules/serialize-javascript": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", "dev": true, - "requires": { + "dependencies": { "randombytes": "^2.1.0" } }, - "setimmediate": { + "node_modules/setimmediate": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/setimmediate/-/setimmediate-1.0.5.tgz", "integrity": "sha1-KQy7Iy4waULX1+qbg3Mqt4VvgoU=", "dev": true }, - "shebang-command": { + "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "shebang-regex": "^3.0.0" + }, + "engines": { + "node": ">=8" } }, - "shebang-regex": { + "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "slash": { + "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true + "dev": true, + "license": "MIT", + "engines": { + "node": ">=8" + } }, - "slice-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", - "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "node_modules/string_decoder": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", + "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", "dev": true, - "requires": { - "ansi-styles": "^4.0.0", - "astral-regex": "^2.0.0", - "is-fullwidth-code-point": "^3.0.0" - }, - "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "dependencies": { + "safe-buffer": "~5.1.0" } }, - "sprintf-js": { - "version": "1.0.3", - "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", - "integrity": "sha1-BOaSb2YolTVPPdAVIDYzuFcpfiw=", + "node_modules/string_decoder/node_modules/safe-buffer": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", + "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", "dev": true }, - "string-width": { + "node_modules/string-width": { "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": { + "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" } }, - "string_decoder": { - "version": "1.1.1", - "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.1.1.tgz", - "integrity": "sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==", - "dev": true, - "requires": { - "safe-buffer": "~5.1.0" - }, + "node_modules/stringify-json": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/stringify-json/-/stringify-json-2.0.0.tgz", + "integrity": "sha512-Dhi9+QD3Fb+EUQWVjVjOaa8RcJVNWTIqEkurb6DXFr7sOGIu8bRHbx1G0qGJFJ4pb2y997owHUa2C26REN8rgg==", + "license": "ISC", "dependencies": { - "safe-buffer": { - "version": "5.1.2", - "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.1.2.tgz", - "integrity": "sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==", - "dev": true - } + "map2object": "^1.0.0" + }, + "engines": { + "node": ">=6.5.0" } }, - "strip-ansi": { + "node_modules/strip-ansi": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, - "requires": { + "dependencies": { "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" } }, - "strip-json-comments": { + "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", - "dev": true - }, - "supports-color": { - "version": "5.5.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", - "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", - "dev": true, - "requires": { - "has-flag": "^3.0.0" - } - }, - "table": { - "version": "6.7.1", - "resolved": "https://registry.npmjs.org/table/-/table-6.7.1.tgz", - "integrity": "sha512-ZGum47Yi6KOOFDE8m223td53ath2enHcYLgOCjGr5ngu8bdIARQk6mN/wRMv4yMRcHnCSnHbCEha4sobQx5yWg==", "dev": true, - "requires": { - "ajv": "^8.0.1", - "lodash.clonedeep": "^4.5.0", - "lodash.truncate": "^4.4.2", - "slice-ansi": "^4.0.0", - "string-width": "^4.2.0", - "strip-ansi": "^6.0.0" + "engines": { + "node": ">=8" }, - "dependencies": { - "ajv": { - "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", - "json-schema-traverse": "^1.0.0", - "require-from-string": "^2.0.2", - "uri-js": "^4.2.2" - } - }, - "json-schema-traverse": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", - "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", - "dev": true - } + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, - "text-table": { + "node_modules/text-table": { "version": "0.2.0", "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", - "integrity": "sha1-f17oI66AUgfACvLfSoTsP8+lcLQ=", - "dev": true + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true, + "license": "MIT" }, - "to-regex-range": { + "node_modules/tmp": { + "version": "0.2.3", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.3.tgz", + "integrity": "sha512-nZD7m9iCPC5g0pYmcaxogYKggSfLsdxl8of3Q/oIbqCqLLIO9IAF0GWjX1z9NZRHPiXv8Wex4yDCaZsgEw0Y8w==", + "license": "MIT", + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" } }, - "traverse": { + "node_modules/traverse": { "version": "0.3.9", "resolved": "https://registry.npmjs.org/traverse/-/traverse-0.3.9.tgz", "integrity": "sha1-cXuPIgzAu3tE5AUUwisui7xw2Lk=", - "dev": true + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/ts-md5": { + "version": "1.3.1", + "resolved": "https://registry.npmjs.org/ts-md5/-/ts-md5-1.3.1.tgz", + "integrity": "sha512-DiwiXfwvcTeZ5wCE0z+2A9EseZsztaiZtGrtSaY5JOD7ekPnR/GoIVD5gXZAlK9Na9Kvpo9Waz5rW64WKAWApg==", + "license": "MIT", + "engines": { + "node": ">=12" + } }, - "tslib": { + "node_modules/tslib": { "version": "1.14.1", "resolved": "https://registry.npmjs.org/tslib/-/tslib-1.14.1.tgz", "integrity": "sha512-Xni35NKzjgMrwevysHTCArtLDpPvye8zV/0E4EyYn43P7/7qvQwPh9BGkHewbMulVntbigmcT7rdX3BNo9wRJg==", - "dev": true + "dev": true, + "license": "0BSD" }, - "tsutils": { + "node_modules/tsutils": { "version": "3.21.0", "resolved": "https://registry.npmjs.org/tsutils/-/tsutils-3.21.0.tgz", "integrity": "sha512-mHKK3iUXL+3UF6xL5k0PEhKRUBKPBCv/+RkEOpjRWxxx27KKRBmmA60A9pgOUvMi8GKhRMPEmjBRPzs2W7O1OA==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "tslib": "^1.8.1" + }, + "engines": { + "node": ">= 6" + }, + "peerDependencies": { + "typescript": ">=2.8.0 || >= 3.2.0-dev || >= 3.3.0-dev || >= 3.4.0-dev || >= 3.5.0-dev || >= 3.6.0-dev || >= 3.6.0-beta || >= 3.7.0-dev || >= 3.7.0-beta" } }, - "type-check": { + "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, - "requires": { + "license": "MIT", + "dependencies": { "prelude-ls": "^1.2.1" + }, + "engines": { + "node": ">= 0.8.0" } }, - "type-fest": { + "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", - "dev": true + "dev": true, + "license": "(MIT OR CC0-1.0)", + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } }, - "typescript": { - "version": "4.4.3", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.4.3.tgz", - "integrity": "sha512-4xfscpisVgqqDfPaJo5vkd+Qd/ItkoagnHpufr+i2QCHBsNYp+G7UAoyFl8aPtx879u38wPV65rZ8qbGZijalA==", - "dev": true + "node_modules/typescript": { + "version": "4.9.5", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", + "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "dev": true, + "license": "Apache-2.0", + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=4.2.0" + } + }, + "node_modules/undici-types": { + "version": "5.26.5", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-5.26.5.tgz", + "integrity": "sha512-JlCMO+ehdEIKqlFxk6IfVoAUVmgz7cU7zD/h9XZ0qzeosSHmUJVOzSQvvYSYWXkFXC+IfLKSIffhv0sVZup6pA==", + "dev": true, + "license": "MIT" }, - "unzipper": { + "node_modules/unzipper": { "version": "0.10.11", "resolved": "https://registry.npmjs.org/unzipper/-/unzipper-0.10.11.tgz", "integrity": "sha512-+BrAq2oFqWod5IESRjL3S8baohbevGcVA+teAIOYWM3pDVdseogqbzhhvvmiyQrUNKFUnDMtELW3X8ykbyDCJw==", "dev": true, - "requires": { + "dependencies": { "big-integer": "^1.6.17", "binary": "~0.3.0", "bluebird": "~3.4.1", @@ -1824,163 +2490,192 @@ "setimmediate": "~1.0.4" } }, - "uri-js": { + "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, - "requires": { + "license": "BSD-2-Clause", + "dependencies": { "punycode": "^2.1.0" } }, - "util-deprecate": { + "node_modules/util-deprecate": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "v8-compile-cache": { - "version": "2.3.0", - "resolved": "https://registry.npmjs.org/v8-compile-cache/-/v8-compile-cache-2.3.0.tgz", - "integrity": "sha512-l8lCEmLcLYZh4nbunNZvQCJc5pv7+RCwa8q/LdUx8u7lsWvPDKmpodJAJNwkAhJC//dFY48KuIEmjtd4RViDrA==", - "dev": true - }, - "vscode-test": { + "node_modules/vscode-test": { "version": "1.6.1", "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz", "integrity": "sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==", + "deprecated": "This package has been renamed to @vscode/test-electron, please update to the new name", "dev": true, - "requires": { + "dependencies": { "http-proxy-agent": "^4.0.1", "https-proxy-agent": "^5.0.0", "rimraf": "^3.0.2", "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" } }, - "which": { + "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, - "requires": { + "dependencies": { "isexe": "^2.0.0" + }, + "bin": { + "node-which": "bin/node-which" + }, + "engines": { + "node": ">= 8" } }, - "wide-align": { + "node_modules/wide-align": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", "dev": true, - "requires": { + "dependencies": { "string-width": "^1.0.2 || 2" + } + }, + "node_modules/wide-align/node_modules/ansi-regex": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", + "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/is-fullwidth-code-point": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", + "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/string-width": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", + "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", + "dev": true, + "dependencies": { + "is-fullwidth-code-point": "^2.0.0", + "strip-ansi": "^4.0.0" }, + "engines": { + "node": ">=4" + } + }, + "node_modules/wide-align/node_modules/strip-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", + "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", + "dev": true, "dependencies": { - "ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true - }, - "is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true - }, - "string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "requires": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - } - }, - "strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "requires": { - "ansi-regex": "^3.0.0" - } - } + "ansi-regex": "^3.0.0" + }, + "engines": { + "node": ">=4" } }, - "word-wrap": { - "version": "1.2.3", - "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.3.tgz", - "integrity": "sha512-Hz/mrNwitNRh/HUAtM/VT/5VH+ygD6DV7mYKZAtHOrbs8U7lvPS6xf7EJKMF0uW1KJCl0H701g3ZGus+muE5vQ==", - "dev": true + "node_modules/word-wrap": { + "version": "1.2.5", + "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", + "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", + "dev": true, + "license": "MIT", + "engines": { + "node": ">=0.10.0" + } }, - "workerpool": { + "node_modules/workerpool": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", "dev": true }, - "wrap-ansi": { + "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, - "requires": { + "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/wrap-ansi/node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, "dependencies": { - "ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "requires": { - "color-convert": "^2.0.1" - } - }, - "color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "requires": { - "color-name": "~1.1.4" - } - }, - "color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - } + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" } }, - "wrappy": { + "node_modules/wrap-ansi/node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", "integrity": "sha1-tSQ9jz7BqjXxNkYFvA0QNuMKtp8=", "dev": true }, - "y18n": { + "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", - "dev": true - }, - "yallist": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", - "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yargs": { + "node_modules/yargs": { "version": "16.2.0", "resolved": "https://registry.npmjs.org/yargs/-/yargs-16.2.0.tgz", "integrity": "sha512-D1mvvtDG0L5ft/jGWkLpG1+m0eQxOfaBvTNELraWj22wSVUMWxZUvYgJYcKh6jGGIkJFhH4IZPQhR4TKpc8mBw==", "dev": true, - "requires": { + "dependencies": { "cliui": "^7.0.2", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", @@ -1988,31 +2683,46 @@ "string-width": "^4.2.0", "y18n": "^5.0.5", "yargs-parser": "^20.2.2" + }, + "engines": { + "node": ">=10" } }, - "yargs-parser": { + "node_modules/yargs-parser": { "version": "20.2.4", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + } }, - "yargs-unparser": { + "node_modules/yargs-unparser": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/yargs-unparser/-/yargs-unparser-2.0.0.tgz", "integrity": "sha512-7pRTIA9Qc1caZ0bZ6RYRGbHJthJWuakf+WmHK0rVeLkNrrGhfoabBNdue6kdINI6r4if7ocq9aD/n7xwKOdzOA==", "dev": true, - "requires": { + "dependencies": { "camelcase": "^6.0.0", "decamelize": "^4.0.0", "flat": "^5.0.2", "is-plain-obj": "^2.1.0" + }, + "engines": { + "node": ">=10" } }, - "yocto-queue": { + "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", - "dev": true + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } } } } diff --git a/package.json b/package.json index 19d1d952..6748f00a 100644 --- a/package.json +++ b/package.json @@ -7,7 +7,7 @@ "publisher": "StefanWehr", "icon": "icon.png", "engines": { - "vscode": "^1.49.0" + "vscode": "^1.85.0" }, "keywords": [ "Python", @@ -39,6 +39,10 @@ { "command": "write-your-python-program.run", "title": "Run program" + }, + { + "command": "write-your-python-program.programflow-visualization", + "title": "programflow-visualization" } ], "configuration": { @@ -75,17 +79,25 @@ "watch": "tsc -watch -p ./", "test": "npm run compile && npm run lint" }, + "dependencies": { + "@types/node": "^18.11.13", + "leader-line": "^1.0.7", + "stringify-json": "^2.0.0", + "tmp": "^0.2.3", + "ts-md5": "^1.3.1" + }, "devDependencies": { "@types/glob": "^7.1.4", "@types/mocha": "^8.2.3", - "@types/node": "^14.17.18", - "@types/vscode": "^1.49.0", - "@typescript-eslint/eslint-plugin": "^4.31.2", - "@typescript-eslint/parser": "^4.31.2", - "eslint": "^7.32.0", + "@types/node": "^18.11.13", + "@types/tmp": "^0.2.6", + "@types/vscode": "^1.85.0", + "@typescript-eslint/eslint-plugin": "^5.30.0", + "@typescript-eslint/parser": "^5.30.0", + "eslint": "^8.18.0", "glob": "^7.2.0", "mocha": "^8.4.0", - "typescript": "^4.4.3", + "typescript": "^4.7.4", "vscode-test": "^1.6.1" } } diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py new file mode 100644 index 00000000..373a7d76 --- /dev/null +++ b/python/deps/pytrace-generator/main.py @@ -0,0 +1,337 @@ +import bdb +import copy +from dataclasses import dataclass +import json +import math +import os +import re +import sys +import types + +def eprint(*args, **kwargs): + print(*args, file=sys.stderr, **kwargs) + +type_name_regex = re.compile("") + +# Frame objects: +# https://docs.python.org/3/reference/datamodel.html#frame-objects + +STACK_TYPES = { + int: "int", + float: "float", + bool: "bool", + str: "str", + type(None): "none", + type: "type" +} +HEAP_TYPES = { + list: "list", + tuple: "tuple", + dict: "dict", + set: "set" +} + +def primitive_type(t): + try: + return STACK_TYPES[t] + except KeyError: + return "ref" + +def complex_type(t): + try: + return STACK_TYPES[t] + except KeyError: + try: + return HEAP_TYPES[t] + except KeyError: + return "instance" + +class HeapValue: + def __init__(self, value, value_type=None, key_lookup_table=None): + self.value = value + self.value_type = value_type + if value_type is None: + self.type_str = complex_type(type(value)) + else: + self.type_str = complex_type(value_type) + self.key_lookup_table = key_lookup_table + + def format(self): + value_type = type(self.value) + value_fmt = self.value + if value_type == list: + value_fmt = [v.format() for v in self.value] + elif value_type == dict: + value_fmt = {k: v.format() for k, v in self.value.items()} + + result = { + "type": self.type_str, + "value": value_fmt + } + + if self.key_lookup_table is not None: + key_lookup_table_fmt = {k: v.format() for k, v in self.key_lookup_table.items()} + result["keys"] = key_lookup_table_fmt + + if self.type_str == "instance": + type_name = str(self.value_type) + search_result = type_name_regex.search(type_name) + if search_result is not None: + type_name = search_result.group(1) + result["name"] = type_name + + return result + +class PrimitiveValue: + def __init__(self, value, variable_name=None): + self.variable_name = variable_name + self.type_str = primitive_type(type(value)) + if self.type_str == "ref": + self.value = id(value) + elif type(value) == float and math.isnan(value): + self.value = "NaN" # Hack because NaN could otherwise not be represented in JSON + elif type(value) == float and math.isinf(value): + # Hack because Infinity could otherwise not be represented in JSON + if value > 0: + self.value = "Infinity" + else: + self.value = "Negative Infinity" + else: + self.value = value + + def is_ref(self): + return self.type_str == "ref" + + def format(self): + d = { + "type": self.type_str, + "value": self.value + } + if type(d["value"]) == type: + d["value"] = str(d["value"]) + if self.variable_name is not None: + d["name"] = self.variable_name + return d + +class StackFrame: + def __init__(self, name): + self.name = name + self.values = [] + + def push(self, variable_name, value): + value_index = None + for idx, stack_value in enumerate(self.values): + if stack_value.variable_name == variable_name: + value_index = idx + break + + stack_value = PrimitiveValue(value, variable_name) + if value_index is not None: + self.values[value_index] = stack_value + else: + self.values.append(stack_value) + + def format(self): + return { + "frameName": self.name, + "locals": [value.format() for value in self.values] + } + +class Stack: + def __init__(self): + self.frames = [] + + def push_frame(self, frame): + self.frames.append(StackFrame(frame.f_code.co_qualname)) + + def pop_frame(self): + self.frames.pop() + + def push(self, variable_name, value): + self.frames[-1].push(variable_name, value) + + def format(self): + return [frame.format() for frame in self.frames] + +class Heap: + def __init__(self): + self.memory = {} + + def store(self, address, value): + # This check only works because we are taking heap snapshots + if address in self.memory: + # Prevents cycles due to cyclic references + return + + value_type = type(value) + stored_value = value + key_lookup_table = None + inner_values = [] + if value_type == list or value_type == tuple or value_type == set: + stored_value = [] + for elem in value: + prim_value = PrimitiveValue(elem) + stored_value.append(prim_value) + if prim_value.is_ref(): + inner_values.append(elem) + elif value_type == dict: + stored_value = {} + key_lookup_table = {} + for k, v in value.items(): + key_id = id(k) + + key_value = PrimitiveValue(k) + key_lookup_table[key_id] = key_value + if key_value.is_ref(): + inner_values.append(k) + + prim_value = PrimitiveValue(v) + stored_value[key_id] = prim_value + if prim_value.is_ref(): + inner_values.append(v) + elif value_type == str: + # Leave it as it is + pass + else: + # Assume that there are no primitive types (int, bool, ...) passed to the top level heap + # Only "real" objects are left + stored_value = {} + for field in dir(value): + try: + v = getattr(value, field) + except: + # Just ignore it in case getattr fails + continue + if field.startswith("__") or callable(v): + continue + + prim_value = PrimitiveValue(getattr(value, field)) + stored_value[field] = prim_value + if prim_value.is_ref(): + inner_values.append(v) + self.memory[address] = HeapValue(stored_value, value_type, key_lookup_table) + + # Store inner elements + for inner in inner_values: + self.store(id(inner), inner) + + def format(self): + return {k: v.format() for k, v in self.memory.items()} + +@dataclass +class TraceStep: + line: int + file_path: str + stack: Stack + heap: Heap + + def format(self): + return { + "line": self.line, + "filePath": self.file_path, + "stack": self.stack.format(), + "heap": self.heap.format() + } + + +def generate_heap(frame, ignore): + heap = Heap() + while True: + for variable_name in frame.f_locals: + if variable_name in ignore or variable_name.startswith("__"): + continue + if callable(frame.f_locals[variable_name]) and frame.f_code.co_qualname == "": + # Skip top-level functions + continue + if primitive_type(type(frame.f_locals[variable_name])) != "ref": + continue + value = frame.f_locals[variable_name] + heap.store(id(value), value) + + frame = frame.f_back + if frame is None or frame.f_code.co_qualname == "Bdb.run": + break + + return heap + + +class PyTraceGenerator(bdb.Bdb): + def __init__(self): + super().__init__() + self.stack = Stack() + self.stack_ignore = [] + self.init = False + self.filename = "" + self.skip_until = None + + def trace_dispatch(self, frame, event, arg): + filename = frame.f_code.co_filename + if filename == "": + filename = self.filename + + # Skip built-in modules + # This might not be the best solution. Adjust if required. + skip = False + if self.skip_until is not None: + skip = filename != self.skip_until + elif not filename.startswith(os.path.dirname(self.filename)): + skip = True + self.skip_until = frame.f_back.f_code.co_filename + if self.skip_until == "": + self.skip_until = self.filename + if skip: + return self.trace_dispatch + else: + self.skip_until = None + + line = frame.f_lineno + if not self.init: + self.init = True + for variable_name in frame.f_locals: + self.stack_ignore.append(variable_name) + + if event == "call": + self.stack.push_frame(frame) + elif event == "return": + self.stack.pop_frame() + elif event == "line": + for variable_name in frame.f_locals: + if variable_name in self.stack_ignore or variable_name.startswith("__"): + continue + if type(frame.f_locals[variable_name]) == types.FunctionType: + # Skip functions + continue + self.stack.push(variable_name, frame.f_locals[variable_name]) + heap = generate_heap(frame, self.stack_ignore) + + step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) + + # Output trace + json_str = json.dumps(step.format()).encode('utf-8') + json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) + sys.stdout.buffer.write(json_len) + sys.stdout.buffer.write(json_str) + # TODO exception + + return self.trace_dispatch + + def run_script(self, filename, script_str): + self.filename = os.path.abspath(filename) + self.run(script_str) + + +if len(sys.argv) <= 1: + eprint("not enough arguments") + eprint("usage: python main.py file.py") + exit(1) + +filename = sys.argv[1] +with open(filename, "r") as f: + script_str = f.read() + +# Add a 'pass' at the end to also get the last trace step +# It's a bit hacky but probably the easiest solution +script_str += "\npass\n" + +debugger = PyTraceGenerator() +debugger.run_script(filename, script_str) diff --git a/src/extension.ts b/src/extension.ts index 18e86f98..00098c83 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -6,6 +6,9 @@ import * as fs from 'fs'; import * as path from 'path'; import { Uri } from 'vscode'; +import { getProgFlowVizCallback } from './programflow-visualization/main'; +import { initTraceCache } from './programflow-visualization/trace_cache'; + const extensionId = 'write-your-python-program'; const python3ConfigKey = 'python3Cmd'; const verboseConfigKey = 'verbose'; @@ -126,6 +129,15 @@ function installCmd( installButton(buttonTitle, cmdId); } +function initProgramFlowVisualization(context: vscode.ExtensionContext) { + initTraceCache(context); + const cmdId = extensionId + ".programflow-visualization"; + let disposable = vscode.commands.registerCommand(cmdId, getProgFlowVizCallback(context)); + disposables.push(disposable); + context.subscriptions.push(disposable); + installButton("$(debug-alt-small) Visualize", cmdId); +} + type PythonCmdResult = { kind: "success", cmd: string[] } | { @@ -383,6 +395,8 @@ export function activate(context: vscode.ExtensionContext) { } ); + initProgramFlowVisualization(context); + vscode.window.onDidChangeActiveTextEditor(showHideButtons); showHideButtons(vscode.window.activeTextEditor); diff --git a/src/programflow-visualization/FileHandler.ts b/src/programflow-visualization/FileHandler.ts new file mode 100644 index 00000000..90b0b7c3 --- /dev/null +++ b/src/programflow-visualization/FileHandler.ts @@ -0,0 +1,38 @@ +import stringify from 'stringify-json'; +import * as vscode from 'vscode'; +import util = require('util'); +import path = require('path'); + +export async function getContentOf(fileUri: vscode.Uri): Promise { + return new util.TextDecoder('utf-8').decode(await vscode.workspace.fs.readFile(fileUri)); +} + +export async function deleteFile(file: vscode.Uri) { + await vscode.workspace.fs.delete(file); +} + +export async function duplicateFileAndExtendWithPass( + file: vscode.Uri, + fileContent: string +): Promise { + const fileName = path.basename(file.fsPath).split('.')[0]; + + const tempFileUri = vscode.Uri.file(file.fsPath.replace(`/${fileName}.`, `/${fileName}_debug.`)); + const utf8Content = new util.TextEncoder().encode(fileContent.concat('\npass')); + + await vscode.workspace.fs.writeFile(tempFileUri, utf8Content); + + return tempFileUri; +} + +export async function createBackendTraceOutput(backendTrace: BackendTrace, file: vscode.Uri) { + const fileName = path.basename(file.fsPath).split('.')[0]; + await vscode.workspace.fs.writeFile( + vscode.Uri.parse(file.fsPath.replace(path.basename(file.fsPath), `backend_trace_${fileName}.json`)), + new util.TextEncoder().encode(stringify(backendTrace)) + ); +} + +export async function writeBackendTrace(backendTrace: BackendTrace, path: string) { + await vscode.workspace.fs.writeFile(vscode.Uri.file(path), new util.TextEncoder().encode(stringify(backendTrace))); +} diff --git a/src/programflow-visualization/backend/backend.ts b/src/programflow-visualization/backend/backend.ts new file mode 100644 index 00000000..2cdbc995 --- /dev/null +++ b/src/programflow-visualization/backend/backend.ts @@ -0,0 +1,16 @@ +import { ExtensionContext, Uri } from 'vscode'; +import { MessagePort, Worker } from 'worker_threads'; +import path = require('path'); + +export function startBackend(context: ExtensionContext, file: Uri): MessagePort { + const mainPath = context.asAbsolutePath("python/deps/pytrace-generator/main.py"); + const workerPath = path.resolve(__dirname, 'trace_generator.js'); + const worker = new Worker(workerPath); + const channel = new MessageChannel(); + worker.postMessage({ + file: file.fsPath, + mainPath: mainPath, + tracePort: channel.port1 + }, [channel.port1]); + return channel.port2; +} diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts new file mode 100644 index 00000000..d633156f --- /dev/null +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -0,0 +1,55 @@ +import { spawn } from 'child_process'; +import { MessagePort, isMainThread, parentPort } from 'worker_threads'; + +export function generateTrace(mainPath: string, filePath: string, tracePort: MessagePort) { + const traceArgs = [mainPath, filePath]; + + const child = spawn("python3", traceArgs, { windowsHide: true }); + let err = ""; + + tracePort.on('close', () => { + child.kill(); + }); + + let buffer = Buffer.alloc(0); + + child.stdout.on('data', (data) => { + buffer = Buffer.concat([buffer, data]); + while (buffer.length >= 4) { + const dataLen = buffer.readUint32BE(0); + if (buffer.length >= 4 + dataLen) { + const traceStr = buffer.subarray(4, 4 + dataLen).toString(); + try { + const traceElem = JSON.parse(traceStr); + tracePort.postMessage(traceElem); + buffer = buffer.subarray(4 + dataLen); + } catch (error) { + console.error("JSON parsing of trace element failed:", error); + tracePort.close(); + child.kill(); + } + } else { + break; + } + } + }); + child.stderr.on('data', (data) => { + err += data.toString(); + }); + child.on('close', (code) => { + if (code !== 0) { + console.error(`trace generator failed with code ${code}: ${err}`); + } + tracePort.close(); + }); + child.on('error', (error) => { + console.error(error); + tracePort.close(); + }); +} + +if (!isMainThread && parentPort) { + parentPort.once('message', (initParams) => { + generateTrace(initParams.mainPath, initParams.file, initParams.tracePort); + }); +} diff --git a/src/programflow-visualization/constants.ts b/src/programflow-visualization/constants.ts new file mode 100644 index 00000000..96d36c1d --- /dev/null +++ b/src/programflow-visualization/constants.ts @@ -0,0 +1,11 @@ +import * as vscode from 'vscode'; + +export namespace Variables { + export const CACHED_TRACES = 'programflow-visualization.cached-traces'; + export const TRACE_CACHE_DIR = 'programflow-visualization.trace-cache-dir'; +} + +export const nextLineExecuteHighlightType = vscode.window.createTextEditorDecorationType({ + backgroundColor: 'rgba(255, 255, 0, 0.25)', // Yellow + isWholeLine: true, +}); diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts new file mode 100644 index 00000000..07cf024a --- /dev/null +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -0,0 +1,166 @@ +function escapeHTML(s: any) { + return s.toString().replace(/[^0-9A-Za-z ]/g, (c: string) => "&#" + c.charCodeAt(0) + ";"); +} + +export class HTMLGenerator { + uniqueId: number = -1; + + constructor() { + } + + generateHTML(traceElement: BackendTraceElem): FrontendTraceElem { + this.uniqueId = -1; + + const frameItems = ` +
+ ${traceElement.stack.map((stackElem, index) => this.frameItem(index, stackElem)).join('')} +
+ `; + + const keys = Array.from(Object.keys(traceElement.heap)); + const values = Array.from(Object.values(traceElement.heap)); + const objectItems = ` +
+ ${keys.map((name, index) => this.objectItem(name, values[index])).join('')} +
+ `; + return [traceElement.line, frameItems, objectItems, traceElement.filePath]; + } + + private objectItem(name: string, value: HeapValue): string { + let headline: string; + switch (value.type) { + case 'instance': + headline = value.name; + break; + default: + headline = value.type; + } + + return ` +
+
${escapeHTML(headline)}
+
${this.heapValue(name, value)}
+
+ `; + } + + private heapValue(name: string, heapValue: HeapValue): string { + let result = ''; + switch (heapValue.type) { + case 'dict': + const metaKeys = Array.from(Object.keys(heapValue.keys)); + // keys and value are not really Maps but rather objects + // -> Conversion required + const keyMap = new Map(Object.entries(heapValue.keys)); + const valueMap = new Map(Object.entries(heapValue.value)); + result = ` +
+ ${metaKeys.map(metaKey => this.dictValue(keyMap.get(metaKey)!, valueMap.get(metaKey)!)).join('')} +
+ `; + break; + case 'instance': + const instanceKeys = Array.from(Object.keys(heapValue.value)); + const instanceValues = Array.from(Object.values(heapValue.value)); // maybe endpointer look for if its exist and if add a second number or key or smth + result = ` +
+ ${instanceKeys.map((key, index) => this.dictValue(key, instanceValues[index])).join('')} +
+ `; + break; + case 'set': + result = ` +
+ ${heapValue.value.map((v, i) => this.setValue(v)).join('')} +
+ `; + break; + /* tuple, list, int[], int[][], ...*/ + default: + result = ` +
+ ${heapValue.value.map((v, i) => this.listValue(v, i)).join('')} +
+ `; + break; + } + return result; + } + + // TODO: escape all values for embedding them into HTML + + private dictValue(key: any, value: Value): string { + this.uniqueId++; + return ` +
+
+ ${key.type === 'ref' ? '' : escapeHTML(key.value ? key.value : key)} +
+
+ ${value.type === 'ref' ? '' : escapeHTML(value.value)} +
+
+ `; + } + + private listValue(value: Value, index: number): string { + this.uniqueId++; + return ` +
+
+ ${index} +
+
+ ${value.type === 'ref' ? '' : escapeHTML(value.value)} +
+
+ `; + } + + private setValue(value: Value): string { + this.uniqueId++; + return ` +
+
+ ${value.type === 'ref' ? '' : escapeHTML(value.value)} +
+
+ `; + } + + private frameItem(index: number, stackElem: StackElem): string { + return ` +
+
+ ${stackElem.frameName === '' ? 'Global' : escapeHTML(stackElem.frameName)} +
+
+ ${stackElem.locals.map(namedValue => this.frameSubItem(stackElem.frameName, namedValue)).join('')} +
+
+ `; + } + + private frameSubItem(frameName: string, namedValue: NamedValue): string { + return ` +
+
+ ${escapeHTML(namedValue.name)} +
+
+ ${this.getCorrectValueOf(namedValue)} +
+
+ `; + } + + private getCorrectValueOf(value: Value): string { + switch (value.type) { + case 'ref': + return ''; + default: + return escapeHTML(`${value.value}`); + } + } +} diff --git a/src/programflow-visualization/frontend/frontend.ts b/src/programflow-visualization/frontend/frontend.ts new file mode 100644 index 00000000..c1703ae3 --- /dev/null +++ b/src/programflow-visualization/frontend/frontend.ts @@ -0,0 +1,23 @@ +import { ExtensionContext } from 'vscode'; +import { VisualizationPanel } from './visualization_panel'; +import { MessagePort } from 'worker_threads'; +import * as TraceCache from '../trace_cache'; + +export async function startFrontend( + context: ExtensionContext, + fileHash: string, + tracePort: MessagePort | null): Promise { + var trace: BackendTrace = []; + if (await TraceCache.traceAlreadyExists(context, fileHash)) { + trace = await TraceCache.getTrace(context, fileHash); + } + + const panel = await VisualizationPanel.getVisualizationPanel(context, fileHash, trace, tracePort); + if (!panel) { + return failure("Frontend couldn't be initialized!"); + } +} + +function failure(errorMessage: string): Failure { + return { errorMessage: errorMessage } as Failure; +} diff --git a/src/programflow-visualization/frontend/resources/leader-line.min.js b/src/programflow-visualization/frontend/resources/leader-line.min.js new file mode 100644 index 00000000..b3f2430b --- /dev/null +++ b/src/programflow-visualization/frontend/resources/leader-line.min.js @@ -0,0 +1,2 @@ +/*! LeaderLine v1.0.7 (c) anseki https://anseki.github.io/leader-line/ */ +var LeaderLine=function(){"use strict";var Z,w,O,M,I,o,t,s,h,u,n,a,e,_,v,l,r,i,E,x,p,c,d,C="leader-line",b=1,k=2,L=3,A=4,V={top:b,right:k,bottom:L,left:A},P=1,N=2,T=3,W=4,B=5,R={straight:P,arc:N,fluid:T,magnet:W,grid:B},Y="behind",f=C+"-defs",y='',X={disc:{elmId:"leader-line-disc",noRotate:!0,bBox:{left:-5,top:-5,width:10,height:10,right:5,bottom:5},widthR:2.5,heightR:2.5,bCircle:5,sideLen:5,backLen:5,overhead:0,outlineBase:1,outlineMax:4},square:{elmId:"leader-line-square",noRotate:!0,bBox:{left:-5,top:-5,width:10,height:10,right:5,bottom:5},widthR:2.5,heightR:2.5,bCircle:5,sideLen:5,backLen:5,overhead:0,outlineBase:1,outlineMax:4},arrow1:{elmId:"leader-line-arrow1",bBox:{left:-8,top:-8,width:16,height:16,right:8,bottom:8},widthR:4,heightR:4,bCircle:8,sideLen:8,backLen:8,overhead:8,outlineBase:2,outlineMax:1.5},arrow2:{elmId:"leader-line-arrow2",bBox:{left:-7,top:-8,width:11,height:16,right:4,bottom:8},widthR:2.75,heightR:4,bCircle:8,sideLen:8,backLen:7,overhead:4,outlineBase:1,outlineMax:1.75},arrow3:{elmId:"leader-line-arrow3",bBox:{left:-4,top:-5,width:12,height:10,right:8,bottom:5},widthR:3,heightR:2.5,bCircle:8,sideLen:5,backLen:4,overhead:8,outlineBase:1,outlineMax:2.5},hand:{elmId:"leader-line-hand",bBox:{left:-3,top:-12,width:40,height:24,right:37,bottom:12},widthR:10,heightR:6,bCircle:37,sideLen:12,backLen:3,overhead:37},crosshair:{elmId:"leader-line-crosshair",noRotate:!0,bBox:{left:-96,top:-96,width:192,height:192,right:96,bottom:96},widthR:48,heightR:48,bCircle:96,sideLen:96,backLen:96,overhead:0}},F={behind:Y,disc:"disc",square:"square",arrow1:"arrow1",arrow2:"arrow2",arrow3:"arrow3",hand:"hand",crosshair:"crosshair"},q={disc:"disc",square:"square",arrow1:"arrow1",arrow2:"arrow2",arrow3:"arrow3",hand:"hand",crosshair:"crosshair"},G=[b,k,L,A],D="auto",Q={x:"left",y:"top",width:"width",height:"height"},z=80,j=4,H=5,U=120,K=8,J=3.75,$=10,ee=30,te=.5522847,ne=.25*Math.PI,m=/^\s*(\-?[\d\.]+)\s*(\%)?\s*$/,ae="http://www.w3.org/2000/svg",S="-ms-scroll-limit"in document.documentElement.style&&"-ms-ime-align"in document.documentElement.style&&!window.navigator.msPointerEnabled,ie=!S&&!!document.uniqueID,oe="MozAppearance"in document.documentElement.style,le=!(S||oe||!window.chrome||!window.CSS),re=!S&&!ie&&!oe&&!le&&!window.chrome&&"WebkitAppearance"in document.documentElement.style,se=ie||S?.2:.1,ue={path:T,lineColor:"coral",lineSize:4,plugSE:[Y,"arrow1"],plugSizeSE:[1,1],lineOutlineEnabled:!1,lineOutlineColor:"indianred",lineOutlineSize:.25,plugOutlineEnabledSE:[!1,!1],plugOutlineSizeSE:[1,1]},he=(p={}.toString,c={}.hasOwnProperty.toString,d=c.call(Object),function(e){return e&&"[object Object]"===p.call(e)&&(!(e=Object.getPrototypeOf(e))||(e=e.hasOwnProperty("constructor")&&e.constructor)&&"function"==typeof e&&c.call(e)===d)}),pe=Number.isFinite||function(e){return"number"==typeof e&&window.isFinite(e)},g=(_={ease:[.25,.1,.25,1],linear:[0,0,1,1],"ease-in":[.42,0,1,1],"ease-out":[0,0,.58,1],"ease-in-out":[.42,0,.58,1]},v=1e3/60/2,l=window.requestAnimationFrame||window.mozRequestAnimationFrame||window.webkitRequestAnimationFrame||window.msRequestAnimationFrame||function(e){setTimeout(e,v)},r=window.cancelAnimationFrame||window.mozCancelAnimationFrame||window.webkitCancelAnimationFrame||window.msCancelAnimationFrame||function(e){clearTimeout(e)},i=Number.isFinite||function(e){return"number"==typeof e&&window.isFinite(e)},E=[],x=0,{add:function(n,e,t,a,i,o,l){var r,s,u,h,p,c,d,f,y,m,S=++x;function g(e,t){return{value:n(t),timeRatio:e,outputRatio:t}}if("string"==typeof i&&(i=_[i]),n=n||function(){},t=this._endIndex||this._string[this._currentIndex]<"0"||"9"=this._endIndex||this._string[this._currentIndex]<"0"||"9"=this._endIndex)return null;var e=null,t=this._string[this._currentIndex];if(this._currentIndex+=1,"0"===t)e=0;else{if("1"!==t)return null;e=1}return this._skipOptionalSpacesOrDelimiter(),e}};function n(e){if(!e||0===e.length)return[];var t=new i(e),n=[];if(t.initialCommandIsMoveTo())for(;t.hasMoreData();){var a=t.parseSegment();if(null===a)break;n.push(a)}return n}function l(e){return e.map(function(e){return{type:e.type,values:Array.prototype.slice.call(e.values)}})}function r(e){var u=[],h=null,p=null,c=null,d=null,f=null,y=null,m=null;return e.forEach(function(e){var t,n,a,i,o,l,r,s;"M"===e.type?(r=e.values[0],s=e.values[1],u.push({type:"M",values:[r,s]}),d=y=r,f=m=s):"C"===e.type?(o=e.values[0],l=e.values[1],t=e.values[2],n=e.values[3],r=e.values[4],s=e.values[5],u.push({type:"C",values:[o,l,t,n,r,s]}),p=t,c=n,d=r,f=s):"L"===e.type?(r=e.values[0],s=e.values[1],u.push({type:"L",values:[r,s]}),d=r,f=s):"H"===e.type?(r=e.values[0],u.push({type:"L",values:[r,f]}),d=r):"V"===e.type?(s=e.values[0],u.push({type:"L",values:[d,s]}),f=s):"S"===e.type?(t=e.values[0],n=e.values[1],r=e.values[2],s=e.values[3],i="C"===h||"S"===h?(a=d+(d-p),f+(f-c)):(a=d,f),u.push({type:"C",values:[a,i,t,n,r,s]}),p=t,c=n,d=r,f=s):"T"===e.type?(r=e.values[0],s=e.values[1],l="Q"===h||"T"===h?(o=d+(d-p),f+(f-c)):(o=d,f),u.push({type:"C",values:[a=d+2*(o-d)/3,i=f+2*(l-f)/3,r+2*(o-r)/3,s+2*(l-s)/3,r,s]}),p=o,c=l,d=r,f=s):"Q"===e.type?(o=e.values[0],l=e.values[1],r=e.values[2],s=e.values[3],u.push({type:"C",values:[a=d+2*(o-d)/3,i=f+2*(l-f)/3,r+2*(o-r)/3,s+2*(l-s)/3,r,s]}),p=o,c=l,d=r,f=s):"A"===e.type?(n=e.values[0],a=e.values[1],i=e.values[2],o=e.values[3],l=e.values[4],r=e.values[5],s=e.values[6],0===n||0===a?(u.push({type:"C",values:[d,f,r,s,r,s]}),d=r,f=s):d===r&&f===s||b(d,f,r,s,n,a,i,o,l).forEach(function(e){u.push({type:"C",values:e}),d=r,f=s})):"Z"===e.type&&(u.push(e),d=y,f=m),h=e.type}),u}var s=e.SVGPathElement.prototype.setAttribute,u=e.SVGPathElement.prototype.removeAttribute,d=e.Symbol?e.Symbol():"__cachedPathData",f=e.Symbol?e.Symbol():"__cachedNormalizedPathData",b=function(e,t,n,a,i,o,l,r,s,u){function h(e,t,n){return{x:e*Math.cos(n)-t*Math.sin(n),y:e*Math.sin(n)+t*Math.cos(n)}}var p=Math.PI*l/180,c=[];u?(_=u[0],v=u[1],S=u[2],g=u[3]):(e=(m=h(e,t,-p)).x,t=m.y,1<(m=(y=(e-(n=(f=h(n,a,-p)).x))/2)*y/(i*i)+(d=(t-(a=f.y))/2)*d/(o*o))&&(i*=m=Math.sqrt(m),o*=m),f=i*i,m=o*o,S=(f=(r===s?-1:1)*Math.sqrt(Math.abs((f*m-f*d*d-m*y*y)/(f*d*d+m*y*y))))*i*d/o+(e+n)/2,g=f*-o*y/i+(t+a)/2,_=Math.asin(parseFloat(((t-g)/o).toFixed(9))),v=Math.asin(parseFloat(((a-g)/o).toFixed(9))),e120*Math.PI/180&&(d=v,f=n,y=a,v=s&&_=e.duration&&e.count&&e.loopsLeft<=1)return a=e.frames[e.lastFrame=e.reverse?0:e.frames.length-1],e.frameCallback(a.value,!0,a.timeRatio,a.outputRatio),void(e.framesStart=null);if(t>e.duration){if(n=Math.floor(t/e.duration),e.count){if(n>=e.loopsLeft)return a=e.frames[e.lastFrame=e.reverse?0:e.frames.length-1],e.frameCallback(a.value,!0,a.timeRatio,a.outputRatio),void(e.framesStart=null);e.loopsLeft-=n}e.framesStart+=e.duration*n,t=i-e.framesStart}e.reverse&&(t=e.duration-t),a=e.frames[e.lastFrame=Math.round(t/v)],!1!==e.frameCallback(a.value,!1,a.timeRatio,a.outputRatio)?o=!0:e.framesStart=null}}),o&&(e=l.call(window,xe))}function be(e,t){e.framesStart=Date.now(),null!=t&&(e.framesStart-=e.duration*(e.reverse?1-t:t)),e.loopsLeft=e.count,e.lastFrame=null,xe()}function ke(t,n){var e,a;return typeof t!=typeof n||(e=he(t)?"obj":Array.isArray(t)?"array":"")!=(he(n)?"obj":Array.isArray(n)?"array":"")||("obj"===e?ke(a=Object.keys(t).sort(),Object.keys(n).sort())||a.some(function(e){return ke(t[e],n[e])}):"array"===e?t.length!==n.length||t.some(function(e,t){return ke(e,n[t])}):t!==n)}function we(n){return n&&(he(n)?Object.keys(n).reduce(function(e,t){return e[t]=we(n[t]),e},{}):Array.isArray(n)?n.map(we):n)}function Oe(e){var t,n,a,i=1,o=e=(e+"").trim();function l(e){var t=1,e=m.exec(e);return e&&(t=parseFloat(e[1]),e[2]?t=0<=t&&t<=100?t/100:1:(t<0||1=Math.abs(n)?0<=t?k:A:0<=n?L:b))})),f.position_path!==y.position_path||f.position_lineStrokeWidth!==y.position_lineStrokeWidth||[0,1].some(function(e){return f.position_plugOverheadSE[e]!==y.position_plugOverheadSE[e]||(t=v[e],n=y.position_socketXYSE[e],t.x!==n.x||t.y!==n.y||t.socketId!==n.socketId)||(t=g[e],n=y.position_socketGravitySE[e],(e=null==t?"auto":Array.isArray(t)?"array":"number")!=(null==n?"auto":Array.isArray(n)?"array":"number")||("array"==e?t[0]!==n[0]||t[1]!==n[1]:t!==n));var t,n})){switch(l.pathList.baseVal=_=[],l.pathList.animVal=null,f.position_path){case P:_.push([E(v[0]),E(v[1])]);break;case N:t="number"==typeof g[0]&&0j?(f.position_lineStrokeWidth-j)*H:0),e.socketId===b?{x:0,y:-(i=(i=(e.y-n.y)/2)=t.x:t.dirId===r?e.y>=t.y:e.x<=t.x}function f(e,t){return t.dirId===i||t.dirId===r?e.x===t.x:e.y===t.y}function y(e){return e[0]?{contain:0,notContain:1}:{contain:1,notContain:0}}function m(e,t,n){return Math.abs(t[n]-e[n])}function S(e,t,n){return"x"===n?e.x=ee?S(u[t.notContain],u[t.contain],o[t.contain]):u[t.contain].dirId)):(i=[{x:u[0].x,y:u[0].y},{x:u[1].x,y:u[1].y}],s.forEach(function(e,t){var n=0===t?1:0,a=m(i[t],i[n],o[t]);a$&&(d[a]-e<$&&(e=d[a]-$),s=Ve(n[0],n[1],(p?e:d[a]-e)/d[a]),_[a]=p?[s,n[1]]:[n[0],s],d[a]-=e)):(d[a]=d[a]||Te.apply(null,n),d[a]>$&&(d[a]-e<$&&(e=d[a]-$),s=Ne(n[0],n[1],n[2],n[3],We(n[0],n[1],n[2],n[3],p?e:d[a]-e)),o=p?(i=n[0],s.toP1):(i=n[3],s.fromP2),l=Math.atan2(i.y-s.y,s.x-i.x),r=Ae(s,o),s.x=i.x+Math.cos(l)*e,s.y=i.y+Math.sin(l)*e*-1,o.x=s.x+Math.cos(l)*r,o.y=s.y+Math.sin(l)*r*-1,_[a]=p?[s,s.toP1,s.toP2,n[3]]:[n[0],s.fromP1,s.fromP2,s],d[a]=null)):e<0&&(n=_[a=p?0:_.length-1],s=v[t].socketId,t=-c[t]["x"==(u=s===A||s===k?"x":"y")?"width":"height"],h=(e=en.outlineMax&&(t=n.outlineMax),t*=2*n.outlineBase,y=Xe(r,u.plugOutline_strokeWidthSE,e,t)||y,y=Xe(r,u.plugOutline_inStrokeWidthSE,e,u.plugOutline_colorTraSE[e]?t-se/(u.line_strokeWidth/ue.lineSize)/s.plugSizeSE[e]*2:t/2)||y)}),y)),(t.faces||U.line||U.plug||U.lineOutline||U.plugOutline)&&(U.faces=(g=(m=e).curStats,_=m.aplStats,v=m.events,E=!1,!g.line_altColor&&Xe(m,_,"line_color",S=g.line_color,v.apl_line_color)&&(m.lineFace.style.stroke=S,E=!0),Xe(m,_,"line_strokeWidth",S=g.line_strokeWidth,v.apl_line_strokeWidth)&&(m.lineShape.style.strokeWidth=S+"px",E=!0,(oe||ie)&&(je(m,m.lineShape),ie&&(je(m,m.lineFace),je(m,m.lineMaskCaps)))),Xe(m,_,"lineOutline_enabled",S=g.lineOutline_enabled,v.apl_lineOutline_enabled)&&(m.lineOutlineFace.style.display=S?"inline":"none",E=!0),g.lineOutline_enabled&&(Xe(m,_,"lineOutline_color",S=g.lineOutline_color,v.apl_lineOutline_color)&&(m.lineOutlineFace.style.stroke=S,E=!0),Xe(m,_,"lineOutline_strokeWidth",S=g.lineOutline_strokeWidth,v.apl_lineOutline_strokeWidth)&&(m.lineOutlineMaskShape.style.strokeWidth=S+"px",E=!0,ie&&(je(m,m.lineOutlineMaskCaps),je(m,m.lineOutlineFace))),Xe(m,_,"lineOutline_inStrokeWidth",S=g.lineOutline_inStrokeWidth,v.apl_lineOutline_inStrokeWidth)&&(m.lineMaskShape.style.strokeWidth=S+"px",E=!0,ie&&(je(m,m.lineOutlineMaskCaps),je(m,m.lineOutlineFace)))),Xe(m,_,"plug_enabled",S=g.plug_enabled,v.apl_plug_enabled)&&(m.plugsFace.style.display=S?"inline":"none",E=!0),g.plug_enabled&&[0,1].forEach(function(n){var e=g.plug_plugSE[n],t=e!==Y?X[q[e]]:null,a=Ze(n,t);Xe(m,_.plug_enabledSE,n,S=g.plug_enabledSE[n],v.apl_plug_enabledSE)&&(m.plugsFace.style[a.prop]=S?"url(#"+m.plugMarkerIdSE[n]+")":"none",E=!0),g.plug_enabledSE[n]&&(Xe(m,_.plug_plugSE,n,e,v.apl_plug_plugSE)&&(m.plugFaceSE[n].href.baseVal="#"+t.elmId,Ue(m,m.plugMarkerSE[n],a.orient,t.bBox,m.svg,m.plugMarkerShapeSE[n],m.plugsFace),E=!0,oe&&je(m,m.plugsFace)),Xe(m,_.plug_colorSE,n,S=g.plug_colorSE[n],v.apl_plug_colorSE)&&(m.plugFaceSE[n].style.fill=S,E=!0,(le||re||ie)&&!g.line_colorTra&&je(m,ie?m.lineMaskCaps:m.capsMaskLine)),["markerWidth","markerHeight"].forEach(function(e){var t="plug_"+e+"SE";Xe(m,_[t],n,S=g[t][n],v["apl_"+t])&&(m.plugMarkerSE[n][e].baseVal.value=S,E=!0)}),Xe(m,_.plugOutline_enabledSE,n,S=g.plugOutline_enabledSE[n],v.apl_plugOutline_enabledSE)&&(S?(m.plugFaceSE[n].style.mask="url(#"+m.plugMaskIdSE[n]+")",m.plugOutlineFaceSE[n].style.display="inline"):(m.plugFaceSE[n].style.mask="none",m.plugOutlineFaceSE[n].style.display="none"),E=!0),g.plugOutline_enabledSE[n]&&(Xe(m,_.plugOutline_plugSE,n,e,v.apl_plugOutline_plugSE)&&(m.plugOutlineFaceSE[n].href.baseVal=m.plugMaskShapeSE[n].href.baseVal=m.plugOutlineMaskShapeSE[n].href.baseVal="#"+t.elmId,[m.plugMaskSE[n],m.plugOutlineMaskSE[n]].forEach(function(e){e.x.baseVal.value=t.bBox.left,e.y.baseVal.value=t.bBox.top,e.width.baseVal.value=t.bBox.width,e.height.baseVal.value=t.bBox.height}),E=!0),Xe(m,_.plugOutline_colorSE,n,S=g.plugOutline_colorSE[n],v.apl_plugOutline_colorSE)&&(m.plugOutlineFaceSE[n].style.fill=S,E=!0,ie&&(je(m,m.lineMaskCaps),je(m,m.lineOutlineMaskCaps))),Xe(m,_.plugOutline_strokeWidthSE,n,S=g.plugOutline_strokeWidthSE[n],v.apl_plugOutline_strokeWidthSE)&&(m.plugOutlineMaskShapeSE[n].style.strokeWidth=S+"px",E=!0),Xe(m,_.plugOutline_inStrokeWidthSE,n,S=g.plugOutline_inStrokeWidthSE[n],v.apl_plugOutline_inStrokeWidthSE)&&(m.plugMaskShapeSE[n].style.strokeWidth=S+"px",E=!0)))}),E)),(t.position||U.line||U.plug)&&(U.position=Ke(e)),(t.path||U.position)&&(U.path=(k=(x=e).curStats,I=x.aplStats,M=x.pathList.animVal||x.pathList.baseVal,w=k.path_edge,C=!1,M&&(w.x1=w.x2=M[0][0].x,w.y1=w.y2=M[0][0].y,k.path_pathData=b=Be(M,function(e){e.xw.x2&&(w.x2=e.x),e.y>w.y2&&(w.y2=e.y)}),Fe(b,I.path_pathData)&&(x.linePath.setPathData(b),I.path_pathData=b,C=!0,ie?(je(x,x.plugsFace),je(x,x.lineMaskCaps)):oe&&je(x,x.linePath),x.events.apl_path&&x.events.apl_path.forEach(function(e){e(x,b)}))),C)),U.viewBox=(M=(O=e).curStats,I=O.aplStats,C=M.path_edge,L=M.viewBox_bBox,A=I.viewBox_bBox,V=O.svg.viewBox.baseVal,P=O.svg.style,N=!1,I=Math.max(M.line_strokeWidth/2,M.viewBox_plugBCircleSE[0]||0,M.viewBox_plugBCircleSE[1]||0),T={x1:C.x1-I,y1:C.y1-I,x2:C.x2+I,y2:C.y2+I},O.events.new_edge4viewBox&&O.events.new_edge4viewBox.forEach(function(e){e(O,T)}),L.x=M.lineMask_x=M.lineOutlineMask_x=M.maskBGRect_x=T.x1,L.y=M.lineMask_y=M.lineOutlineMask_y=M.maskBGRect_y=T.y1,L.width=T.x2-T.x1,L.height=T.y2-T.y1,["x","y","width","height"].forEach(function(e){var t;(t=L[e])!==A[e]&&(V[e]=A[e]=t,P[Q[e]]=t+("x"===e||"y"===e?O.bodyOffset[e]:0)+"px",N=!0)}),N),U.mask=(R=(W=e).curStats,F=W.aplStats,G=!1,R.plug_enabled?[0,1].forEach(function(e){R.capsMaskMarker_enabledSE[e]=R.plug_enabledSE[e]&&R.plug_colorTraSE[e]||R.plugOutline_enabledSE[e]&&R.plugOutline_colorTraSE[e]}):R.capsMaskMarker_enabledSE[0]=R.capsMaskMarker_enabledSE[1]=!1,R.capsMaskMarker_enabled=R.capsMaskMarker_enabledSE[0]||R.capsMaskMarker_enabledSE[1],R.lineMask_outlineMode=R.lineOutline_enabled,R.caps_enabled=R.capsMaskMarker_enabled||R.capsMaskAnchor_enabledSE[0]||R.capsMaskAnchor_enabledSE[1],R.lineMask_enabled=R.caps_enabled||R.lineMask_outlineMode,(R.lineMask_enabled&&!R.lineMask_outlineMode||R.lineOutline_enabled)&&["x","y"].forEach(function(e){var t="maskBGRect_"+e;Xe(W,F,t,B=R[t])&&(W.maskBGRect[e].baseVal.value=B,G=!0)}),Xe(W,F,"lineMask_enabled",B=R.lineMask_enabled)&&(W.lineFace.style.mask=B?"url(#"+W.lineMaskId+")":"none",G=!0,re&&je(W,W.lineMask)),R.lineMask_enabled&&(Xe(W,F,"lineMask_outlineMode",B=R.lineMask_outlineMode)&&(B?(W.lineMaskBG.style.display="none",W.lineMaskShape.style.display="inline"):(W.lineMaskBG.style.display="inline",W.lineMaskShape.style.display="none"),G=!0),["x","y"].forEach(function(e){var t="lineMask_"+e;Xe(W,F,t,B=R[t])&&(W.lineMask[e].baseVal.value=B,G=!0)}),Xe(W,F,"caps_enabled",B=R.caps_enabled)&&(W.lineMaskCaps.style.display=W.lineOutlineMaskCaps.style.display=B?"inline":"none",G=!0,re&&je(W,W.capsMaskLine)),R.caps_enabled&&([0,1].forEach(function(e){var t;Xe(W,F.capsMaskAnchor_enabledSE,e,B=R.capsMaskAnchor_enabledSE[e])&&(W.capsMaskAnchorSE[e].style.display=B?"inline":"none",G=!0,re&&je(W,W.lineMask)),R.capsMaskAnchor_enabledSE[e]&&(Fe(t=R.capsMaskAnchor_pathDataSE[e],F.capsMaskAnchor_pathDataSE[e])&&(W.capsMaskAnchorSE[e].setPathData(t),F.capsMaskAnchor_pathDataSE[e]=t,G=!0),Xe(W,F.capsMaskAnchor_strokeWidthSE,e,B=R.capsMaskAnchor_strokeWidthSE[e])&&(W.capsMaskAnchorSE[e].style.strokeWidth=B+"px",G=!0))}),Xe(W,F,"capsMaskMarker_enabled",B=R.capsMaskMarker_enabled)&&(W.capsMaskLine.style.display=B?"inline":"none",G=!0),R.capsMaskMarker_enabled&&[0,1].forEach(function(n){var e=R.capsMaskMarker_plugSE[n],t=e!==Y?X[q[e]]:null,a=Ze(n,t);Xe(W,F.capsMaskMarker_enabledSE,n,B=R.capsMaskMarker_enabledSE[n])&&(W.capsMaskLine.style[a.prop]=B?"url(#"+W.lineMaskMarkerIdSE[n]+")":"none",G=!0),R.capsMaskMarker_enabledSE[n]&&(Xe(W,F.capsMaskMarker_plugSE,n,e)&&(W.capsMaskMarkerShapeSE[n].href.baseVal="#"+t.elmId,Ue(W,W.capsMaskMarkerSE[n],a.orient,t.bBox,W.svg,W.capsMaskMarkerShapeSE[n],W.capsMaskLine),G=!0,oe&&(je(W,W.capsMaskLine),je(W,W.lineFace))),["markerWidth","markerHeight"].forEach(function(e){var t="capsMaskMarker_"+e+"SE";Xe(W,F[t],n,B=R[t][n])&&(W.capsMaskMarkerSE[n][e].baseVal.value=B,G=!0)}))}))),R.lineOutline_enabled&&["x","y"].forEach(function(e){var t="lineOutlineMask_"+e;Xe(W,F,t,B=R[t])&&(W.lineOutlineMask[e].baseVal.value=B,G=!0)}),G),t.effect&&(j=(D=e).curStats,H=D.aplStats,Object.keys(Z).forEach(function(e){var t=Z[e],n=e+"_enabled",a=e+"_options",e=j[a];Xe(D,H,n,z=j[n])?(z&&(H[a]=we(e)),t[z?"init":"remove"](D)):z&&ke(e,H[a])&&(t.remove(D),H[n]=!0,H[a]=we(e),t.init(D))})),(le||re)&&U.line&&!U.path&&je(e,e.lineShape),le&&U.plug&&!U.line&&je(e,e.plugsFace),He(e)}function et(e,t){return{duration:(pe(e.duration)&&0i.x2&&(i.x2=e.x2),e.y2>i.y2&&(i.y2=e.y2),["x","y"].forEach(function(e){var t,n="dropShadow_"+e;o[n]=t=i[e+"1"],Xe(a,l,n,t)&&(a.efc_dropShadow_elmFilter[e].baseVal.value=t)}))}}},Object.keys(Z).forEach(function(e){var t=Z[e],n=t.stats;n[e+"_enabled"]={iniValue:!1},n[e+"_options"]={hasProps:!0},t.anim&&(n[e+"_animOptions"]={},n[e+"_animId"]={})}),w={none:{defaultAnimOptions:{},init:function(e,t){var n=e.curStats;n.show_animId&&(g.remove(n.show_animId),n.show_animId=null),w.none.start(e,t)},start:function(e,t){w.none.stop(e,!0)},stop:function(e,t,n){var a=e.curStats;return n=null!=n?n:e.aplStats.show_on,a.show_inAnim=!1,t&&Je(e,n),n?1:0}},fade:{defaultAnimOptions:{duration:300,timing:"linear"},init:function(n,e){var t=n.curStats,a=n.aplStats;t.show_animId&&g.remove(t.show_animId),t.show_animId=g.add(function(e){return e},function(e,t){t?w.fade.stop(n,!0):(n.svg.style.opacity=e+"",ie&&(je(n,n.svg),He(n)))},a.show_animOptions.duration,1,a.show_animOptions.timing,null,!1),w.fade.start(n,e)},start:function(e,t){var n,a=e.curStats;a.show_inAnim&&(n=g.stop(a.show_animId)),Je(e,1),a.show_inAnim=!0,g.start(a.show_animId,!e.aplStats.show_on,null!=t?t:n)},stop:function(e,t,n){var a,i=e.curStats;return n=null!=n?n:e.aplStats.show_on,a=i.show_inAnim?g.stop(i.show_animId):n?1:0,i.show_inAnim=!1,t&&(e.svg.style.opacity=n?"":"0",Je(e,n)),a}},draw:{defaultAnimOptions:{duration:500,timing:[.58,0,.42,1]},init:function(n,e){var t=n.curStats,a=n.aplStats,o=n.pathList.baseVal,i=Re(o),l=i.segsLen,r=i.lenAll;t.show_animId&&g.remove(t.show_animId),t.show_animId=g.add(function(e){var t,n,a,i=-1;if(0===e)n=[[o[0][0],o[0][0]]];else if(1===e)n=o;else{for(t=r*e,n=[];t>=l[++i];)n.push(o[i]),t-=l[i];t&&(2===(a=o[i]).length?n.push([a[0],Ve(a[0],a[1],t/l[i])]):(e=Ne(a[0],a[1],a[2],a[3],We(a[0],a[1],a[2],a[3],t)),n.push([a[0],e.fromP1,e.fromP2,e])))}return n},function(e,t){t?w.draw.stop(n,!0):(n.pathList.animVal=e,$e(n,{path:!0}))},a.show_animOptions.duration,1,a.show_animOptions.timing,null,!1),w.draw.start(n,e)},start:function(e,t){var n,a=e.curStats;a.show_inAnim&&(n=g.stop(a.show_animId)),Je(e,1),a.show_inAnim=!0,Ge(e,"apl_position",w.draw.update),g.start(a.show_animId,!e.aplStats.show_on,null!=t?t:n)},stop:function(e,t,n){var a,i=e.curStats;return n=null!=n?n:e.aplStats.show_on,a=i.show_inAnim?g.stop(i.show_animId):n?1:0,i.show_inAnim=!1,t&&(e.pathList.animVal=n?null:[[e.pathList.baseVal[0][0],e.pathList.baseVal[0][0]]],$e(e,{path:!0}),Je(e,n)),a},update:function(e){De(e,"apl_position",w.draw.update),e.curStats.show_inAnim?w.draw.init(e,w.draw.stop(e)):e.aplStats.show_animOptions={}}}},[["start","anchorSE",0],["end","anchorSE",1],["color","lineColor"],["size","lineSize"],["startSocketGravity","socketGravitySE",0],["endSocketGravity","socketGravitySE",1],["startPlugColor","plugColorSE",0],["endPlugColor","plugColorSE",1],["startPlugSize","plugSizeSE",0],["endPlugSize","plugSizeSE",1],["outline","lineOutlineEnabled"],["outlineColor","lineOutlineColor"],["outlineSize","lineOutlineSize"],["startPlugOutline","plugOutlineEnabledSE",0],["endPlugOutline","plugOutlineEnabledSE",1],["startPlugOutlineColor","plugOutlineColorSE",0],["endPlugOutlineColor","plugOutlineColorSE",1],["startPlugOutlineSize","plugOutlineSizeSE",0],["endPlugOutlineSize","plugOutlineSizeSE",1]].forEach(function(e){var t=e[0],n=e[1],a=e[2];Object.defineProperty(ot.prototype,t,{get:function(){var e=null!=a?Se[this._id].options[n][a]:n?Se[this._id].options[n]:Se[this._id].options[t];return null==e?D:we(e)},set:lt(t),enumerable:!0})}),[["path",R],["startSocket",V,"socketSE",0],["endSocket",V,"socketSE",1],["startPlug",F,"plugSE",0],["endPlug",F,"plugSE",1]].forEach(function(e){var a=e[0],i=e[1],o=e[2],l=e[3];Object.defineProperty(ot.prototype,a,{get:function(){var t,n=null!=l?Se[this._id].options[o][l]:o?Se[this._id].options[o]:Se[this._id].options[a];return n?Object.keys(i).some(function(e){return i[e]===n&&(t=e,!0)})?t:new Error("It's broken"):D},set:lt(a),enumerable:!0})}),Object.keys(Z).forEach(function(n){var a=Z[n];Object.defineProperty(ot.prototype,n,{get:function(){var s,e,t=Se[this._id].options[n];return he(t)?(s=t,e=a.optionsConf.reduce(function(e,t){var n,a=t[0],i=t[1],o=t[2],l=t[3],t=t[4],r=null!=t?s[l][t]:l?s[l]:s[i];return e[i]="id"===a?r?Object.keys(o).some(function(e){return o[e]===r&&(n=e,!0)})?n:new Error("It's broken"):D:null==r?D:we(r),e},{}),a.anim&&(e.animation=we(s.animation)),e):t},set:lt(n),enumerable:!0})}),["startLabel","endLabel","middleLabel"].forEach(function(e,n){Object.defineProperty(ot.prototype,e,{get:function(){var e=Se[this._id],t=e.options;return t.labelSEM[n]&&!e.optionIsAttach.labelSEM[n]?_e[t.labelSEM[n]._id].text:t.labelSEM[n]||""},set:lt(e),enumerable:!0})}),ot.prototype.setOptions=function(e){return it(Se[this._id],e),this},ot.prototype.position=function(){return $e(Se[this._id],{position:!0}),this},ot.prototype.remove=function(){var t=Se[this._id],n=t.curStats;Object.keys(Z).forEach(function(e){e+="_animId";n[e]&&g.remove(n[e])}),n.show_animId&&g.remove(n.show_animId),t.attachments.slice().forEach(function(e){at(t,e)}),t.baseWindow&&t.svg&&t.baseWindow.document.body.removeChild(t.svg),delete Se[this._id]},ot.prototype.show=function(e,t){return tt(Se[this._id],!0,e,t),this},ot.prototype.hide=function(e,t){return tt(Se[this._id],!1,e,t),this},o=function(t){t&&_e[t._id]&&(t.boundTargets.slice().forEach(function(e){at(e.props,t,!0)}),t.conf.remove&&t.conf.remove(t),delete _e[t._id])},rt.prototype.remove=function(){var t=this,n=_e[t._id];n&&(n.boundTargets.slice().forEach(function(e){n.conf.removeOption(n,e)}),ze(function(){var e=_e[t._id];e&&(console.error("LeaderLineAttachment was not removed by removeOption"),o(e))}))},M=rt,window.LeaderLineAttachment=M,I=function(e,t){return e instanceof M&&(!(e.isRemoved||t&&_e[e._id].conf.type!==t)||null)},O={pointAnchor:{type:"anchor",argOptions:[{optionName:"element",type:Me}],init:function(e,t){return e.element=O.pointAnchor.checkElement(t.element),e.x=O.pointAnchor.parsePercent(t.x,!0)||[.5,!0],e.y=O.pointAnchor.parsePercent(t.y,!0)||[.5,!0],!0},removeOption:function(e,t){var n=t.props,a={},i=e.element,e=n.options.anchorSE["start"===t.optionName?1:0];i===e&&(i=e===document.body?new M(O.pointAnchor,[i]):document.body),a[t.optionName]=i,it(n,a)},getBBoxNest:function(e,t){var n=Le(e.element,t.baseWindow),a=n.width,t=n.height;return n.width=n.height=0,n.left=n.right=n.left+e.x[0]*(e.x[1]?a:1),n.top=n.bottom=n.top+e.y[0]*(e.y[1]?t:1),n},parsePercent:function(e,t){var n,a,i=!1;return pe(e)?a=e:"string"==typeof e&&(n=m.exec(e))&&n[2]&&(i=0!==(a=parseFloat(n[1])/100)),null!=a&&(t||0<=a)?[a,i]:null},checkElement:function(e){if(null==e)e=document.body;else if(!Me(e))throw new Error("`element` must be Element");return e}},areaAnchor:{type:"anchor",argOptions:[{optionName:"element",type:Me},{optionName:"shape",type:"string"}],stats:{color:{},strokeWidth:{},elementWidth:{},elementHeight:{},elementLeft:{},elementTop:{},pathListRel:{},bBoxRel:{},pathData:{},viewBoxBBox:{hasProps:!0},dashLen:{},dashGap:{}},init:function(a,e){var t,n=[];return a.element=O.pointAnchor.checkElement(e.element),"string"==typeof e.color&&(a.color=e.color.trim()),"string"==typeof e.fillColor&&(a.fill=e.fillColor.trim()),pe(e.size)&&0<=e.size&&(a.size=e.size),e.dash&&(a.dash=!0,pe(e.dash.len)&&0i.right&&(i.right=t),ei.bottom&&(i.bottom=e)):i={left:t,right:t,top:e,bottom:e},o?S.pathListRel.push([o,{x:t,y:e}]):S.pathListRel=[],o={x:t,y:e}}),S.pathListRel.push([]),e=S.strokeWidth/2,e=[{x:i.left-e,y:i.top-e},{x:i.right+e,y:i.bottom+e}],S.bBoxRel={left:e[0].x,top:e[0].y,right:e[1].x,bottom:e[1].y,width:e[1].x-e[0].x,height:e[1].y-e[0].y}}v.pathListRel=v.bBoxRel=!0}return(v.pathListRel||v.elementLeft||v.elementTop)&&(S.pathData=Be(S.pathListRel,function(e){e.x+=n.left,e.y+=n.top})),Xe(t,g,"strokeWidth",a=S.strokeWidth)&&(t.path.style.strokeWidth=a+"px"),Fe(a=S.pathData,g.pathData)&&(t.path.setPathData(a),g.pathData=a,v.pathData=!0),t.dash&&(!v.pathData&&(!v.strokeWidth||t.dashLen&&t.dashGap)||(S.dashLen=t.dashLen||2*S.strokeWidth,S.dashGap=t.dashGap||S.strokeWidth),v.dash=Xe(t,g,"dashLen",S.dashLen)||v.dash,v.dash=Xe(t,g,"dashGap",S.dashGap)||v.dash,v.dash&&(t.path.style.strokeDasharray=g.dashLen+","+g.dashGap)),d=S.viewBoxBBox,f=g.viewBoxBBox,y=t.svg.viewBox.baseVal,m=t.svg.style,d.x=S.bBoxRel.left+n.left,d.y=S.bBoxRel.top+n.top,d.width=S.bBoxRel.width,d.height=S.bBoxRel.height,["x","y","width","height"].forEach(function(e){(a=d[e])!==f[e]&&(y[e]=f[e]=a,m[Q[e]]=a+("x"===e||"y"===e?t.bodyOffset[e]:0)+"px")}),v.strokeWidth||v.pathListRel||v.bBoxRel}},mouseHoverAnchor:{type:"anchor",argOptions:[{optionName:"element",type:Me},{optionName:"showEffectName",type:"string"}],style:{backgroundImage:"url('data:image/svg+xml;charset=utf-8;base64,PHN2ZyB2ZXJzaW9uPSIxLjEiIHhtbG5zPSJodHRwOi8vd3d3LnczLm9yZy8yMDAwL3N2ZyIgd2lkdGg9IjI0IiBoZWlnaHQ9IjI0Ij48cG9seWdvbiBwb2ludHM9IjI0LDAgMCw4IDgsMTEgMCwxOSA1LDI0IDEzLDE2IDE2LDI0IiBmaWxsPSJjb3JhbCIvPjwvc3ZnPg==')",backgroundSize:"",backgroundRepeat:"no-repeat",backgroundColor:"#f8f881",cursor:"default"},hoverStyle:{backgroundImage:"none",backgroundColor:"#fadf8f"},padding:{top:1,right:15,bottom:1,left:2},minHeight:15,backgroundPosition:{right:2,top:2},backgroundSize:{width:12,height:12},dirKeys:[["top","Top"],["right","Right"],["bottom","Bottom"],["left","Left"]],init:function(a,i){var n,t,e,o,l,r,s,u,h,p=O.mouseHoverAnchor,c={};if(a.element=O.pointAnchor.checkElement(i.element),s=a.element,!((u=s.ownerDocument)&&(h=u.defaultView)&&h.HTMLElement&&s instanceof h.HTMLElement))throw new Error("`element` must be HTML element");return p.style.backgroundSize=p.backgroundSize.width+"px "+p.backgroundSize.height+"px",["style","hoverStyle"].forEach(function(e){var n=p[e];a[e]=Object.keys(n).reduce(function(e,t){return e[t]=n[t],e},{})}),"inline"===(n=a.element.ownerDocument.defaultView.getComputedStyle(a.element,"")).display?a.style.display="inline-block":"none"===n.display&&(a.style.display="block"),O.mouseHoverAnchor.dirKeys.forEach(function(e){var t=e[0],e="padding"+e[1];parseFloat(n[e])e.x2&&(e.x2=n.x2),n.y2>e.y2&&(e.y2=n.y2)},newText:function(e,t,n,a,i){var o,l,r=t.createElementNS(ae,"text");return r.textContent=e,[r.x,r.y].forEach(function(e){var t=n.createSVGLength();t.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX,0),e.baseVal.initialize(t)}),"boolean"!=typeof h&&(h="paintOrder"in r.style),i&&!h?(o=t.createElementNS(ae,"defs"),r.id=a,o.appendChild(r),(l=(e=t.createElementNS(ae,"g")).appendChild(t.createElementNS(ae,"use"))).href.baseVal="#"+a,(t=e.appendChild(t.createElementNS(ae,"use"))).href.baseVal="#"+a,(l=l.style).strokeLinejoin="round",{elmPosition:r,styleText:r.style,styleFill:t.style,styleStroke:l,styleShow:e.style,elmsAppend:[o,e]}):(l=r.style,i&&(l.strokeLinejoin="round",l.paintOrder="stroke"),{elmPosition:r,styleText:l,styleFill:l,styleStroke:i?l:null,styleShow:l,elmsAppend:[r]})},getMidPoint:function(e,t){var n,a,i=Re(e),o=i.segsLen,i=i.lenAll,l=-1,r=i/2+(t||0);if(r<=0)return 2===(n=e[0]).length?Ve(n[0],n[1],0):Ne(n[0],n[1],n[2],n[3],0);if(i<=r)return 2===(n=e[e.length-1]).length?Ve(n[0],n[1],1):Ne(n[0],n[1],n[2],n[3],1);for(a=[];r>o[++l];)a.push(e[l]),r-=o[l];return 2===(n=e[l]).length?Ve(n[0],n[1],r/o[l]):Ne(n[0],n[1],n[2],n[3],We(n[0],n[1],n[2],n[3],r))},initSvg:function(t,n){var e,a,i=O.captionLabel.newText(t.text,n.baseWindow.document,n.svg,C+"-captionLabel-"+t._id,t.outlineColor);["elmPosition","styleFill","styleShow","elmsAppend"].forEach(function(e){t[e]=i[e]}),t.isShown=!1,t.styleShow.visibility="hidden",O.captionLabel.textStyleProps.forEach(function(e){null!=t[e]&&(i.styleText[e]=t[e])}),i.elmsAppend.forEach(function(e){n.svg.appendChild(e)}),e=i.elmPosition.getBBox(),t.width=e.width,t.height=e.height,t.outlineColor&&(a=e.height/9,i.styleStroke.strokeWidth=(a=10c?((t=d.points)[1]=Pe(t[0],t[1],-c),d.len=Ae(t[0],t[1])):(d.points=null,d.len=0),e.len>c+n?((t=e.points)[0]=Pe(t[1],t[0],-(c+n)),e.len=Ae(t[0],t[1])):(e.points=null,e.len=0)),e):null}),f.reduce(function(t,e){var n=e.points;return n&&(a&&y(n[0],a)||t.push({type:"M",values:[n[0].x,n[0].y]}),"line"===e.type?t.push({type:"L",values:[n[1].x,n[1].y]}):(n.shift(),n.forEach(function(e){t.push({type:"L",values:[e.x,e.y]})})),a=n[n.length-1]),t},[])},newText:function(e,t,n,a){var i,o,l,r,s=t.createElementNS(ae,"defs"),u=s.appendChild(t.createElementNS(ae,"path"));return u.id=i=n+"-path",(l=(o=t.createElementNS(ae,"text")).appendChild(t.createElementNS(ae,"textPath"))).href.baseVal="#"+i,l.startOffset.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PX,0),l.textContent=e,"boolean"!=typeof h&&(h="paintOrder"in o.style),a&&!h?(o.id=e=n+"-text",s.appendChild(o),(r=(n=t.createElementNS(ae,"g")).appendChild(t.createElementNS(ae,"use"))).href.baseVal="#"+e,(t=n.appendChild(t.createElementNS(ae,"use"))).href.baseVal="#"+e,(r=r.style).strokeLinejoin="round",{elmPosition:o,elmPath:u,elmOffset:l,styleText:o.style,styleFill:t.style,styleStroke:r,styleShow:n.style,elmsAppend:[s,n]}):(r=o.style,a&&(r.strokeLinejoin="round",r.paintOrder="stroke"),{elmPosition:o,elmPath:u,elmOffset:l,styleText:r,styleFill:r,styleStroke:a?r:null,styleShow:r,elmsAppend:[s,o]})},initSvg:function(t,n){var e,a,i,o=O.pathLabel.newText(t.text,n.baseWindow.document,C+"-pathLabel-"+t._id,t.outlineColor);["elmPosition","elmPath","elmOffset","styleFill","styleShow","elmsAppend"].forEach(function(e){t[e]=o[e]}),t.isShown=!1,t.styleShow.visibility="hidden",O.captionLabel.textStyleProps.forEach(function(e){null!=t[e]&&(o.styleText[e]=t[e])}),o.elmsAppend.forEach(function(e){n.svg.appendChild(e)}),o.elmPath.setPathData([{type:"M",values:[0,100]},{type:"h",values:[100]}]),le&&(i=o.elmOffset.href.baseVal,o.elmOffset.href.baseVal=""),e=o.elmPosition.getBBox(),le&&(o.elmOffset.href.baseVal=i),o.styleText.textAnchor=["start","end","middle"][t.semIndex],2!==t.semIndex||t.lineOffset||o.elmOffset.startOffset.baseVal.newValueSpecifiedUnits(SVGLength.SVG_LENGTHTYPE_PERCENTAGE,50),t.height=e.height,t.outlineColor&&(a=e.height/9,o.styleStroke.strokeWidth=(a=10 { + // Data send from extension + const message = event.data; + + switch (message.command) { + case "updateButtons": + document.querySelector("#nextButton").disabled = !message.next; + document.querySelector("#prevButton").disabled = !message.prev; + document.querySelector("#firstButton").disabled = !message.first; + document.querySelector("#lastButton").disabled = !message.last; + break; + case "updateContent": + const traceMax = message.traceLen - 1; + if (traceMax < 0) { + traceMax = 0; + } + document.querySelector("#traceSlider").max = traceMax; + document.querySelector("#traceSlider").value = message.traceIndex; + document.querySelector("#traceMax").innerHTML = "/" + (message.traceComplete ? traceMax.toString() : "?"); + document.querySelector("#indexCounter").innerHTML = message.traceIndex; + updateVisualization(message.traceElem); + updateIntend(message.traceElem); + updateRefArrows(message.traceElem); + break; + } +}); + +/** + * Updates the Visualization in the Webview, with the given BackendTraceElem. + * + * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + */ +function updateVisualization(traceElem) { + const data = ` +
+
+
+ Frames +
+
+
+
+
+ Objects +
+
+
+
+
+
+ ${traceElem[1]} +
+
+ ${traceElem[2]} +
+
+ `; + document.getElementById("viz").innerHTML = data; + document.getElementById("viz").addEventListener("scroll", () => { + updateRefArrows(traceElem); + }); +} + +/** + * Updates the indendation for heap elements, if a other heap element references it. + * + * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + */ +function updateIntend(traceElem) { + const heapTags = traceElem[2].match(/(?<=startPointer)[0-9]+/g); + if (heapTags) { + heapTags.forEach((tag) => { + const element = document.getElementById("objectItem" + tag); + if (element) { element.classList.add("object-intendation"); } + }); + } +} + +/** + * Updates the Reference Arrows from frame items to heap & heap items to heap items. + * + * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + */ +function updateRefArrows(traceElem) { + const tags = getCurrentTags(traceElem); + refTags.forEach((tag) => tag.remove()); + refTags = []; + + if (!tags) { + return; + } + + refTags = tags + .filter((tag) => tag.elem1 && tag.elem2) + .map((tag) => { + return new LeaderLine(tag.elem1, tag.elem2, { + size: 2, + path: "magnet", + startSocket: "right", + endSocket: "left", + startPlug: "square", + startSocketGravity: [50, -10], + endSocketGravity: [-5, -5], + endPlug: "arrow1", + color: getColor(tag), + }); + }); +} + +/** + * Retrieves all id's on the frame side and heap side, that have a potential start or end pointer in there id. + * Is later used to create Reference Arrows. + * + * @param traceElem A BackendTraceElem with 3 fields (line, stack, heap) + * @returns A list with all ids that have either a start or end pointer id in the html + */ +function getCurrentTags(traceElem) { + const stackTags = traceElem[1].match(/(?<=id=")(.+)Pointer[0-9]+/g); + const heapTags = traceElem[2].match(/(?<=startPointer)[0-9]+/g); + const uniqueId = traceElem[2].match(/(?<=)\d+(?=startPointer)/g); + + if (!stackTags) { + return; + } + + const stackRefs = stackTags.map((tag) => { + const id = tag.match(/(?<=.*Pointer)[\d]+/g); + return { + tag: id, + elem1: document.getElementById(tag), + elem2: document.getElementById("heapEndPointer" + id), + }; + }); + + let heapRefs = []; + if (heapTags) { + heapRefs = heapTags.map((reference, index) => { + return { + tag: reference, + elem1: document.getElementById( + uniqueId[index] + "startPointer" + reference + ), + elem2: document.getElementById("heapEndPointer" + reference), + }; + }); + } + + return [...heapRefs, ...stackRefs]; +} + +/** + * Creates a color based on the tag number (variable reference). + * + * @param tag The tag number (variable reference) is used to create a color. + * @returns + */ +function getColor(tag) { + const hue = ((0.618033988749895 + tag.tag / 10) % 1) * 100; + return `hsl(${hue}, 60%, 45%)`; +} + +function onLoad() { + document.querySelector("#nextButton").disabled = false; + document.querySelector("#lastButton").disabled = false; + document.querySelector("#prevButton").disabled = true; + document.querySelector("#firstButton").disabled = true; +} + +async function onClick(type) { + await vscode.postMessage({ command: "onClick", type: type }); +} + +async function onSlide(sliderValue) { + await vscode.postMessage({ command: "onSlide", sliderValue: sliderValue }); +} diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts new file mode 100644 index 00000000..31cbcd13 --- /dev/null +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -0,0 +1,277 @@ +import * as vscode from 'vscode'; +import { nextLineExecuteHighlightType } from '../constants'; +import path = require('path'); +import { HTMLGenerator } from './HTMLGenerator'; +import { MessagePort } from 'worker_threads'; +import { cacheTrace } from '../trace_cache'; + +const FRONTEND_RESOURCE_PATH = 'src/programflow-visualization/frontend/resources'; + +export class VisualizationPanel { + private _panel: vscode.WebviewPanel | undefined; + private readonly _style: vscode.Uri; + private readonly _script: vscode.Uri; + private readonly _lineScript: vscode.Uri; + private readonly _fileHash: string; + private readonly _tracePort: MessagePort | null; + private _backendTrace: PartialBackendTrace; + private _trace: FrontendTrace; + private _traceIndex: number; + private _tracePortSelfClose: boolean; + + private constructor(context: vscode.ExtensionContext, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null) { + this._fileHash = fileHash; + this._tracePort = tracePort; + this._backendTrace = { trace: trace, complete: trace.length > 0 }; + this._trace = []; + const htmlGenerator = new HTMLGenerator(); + trace.forEach(traceElement => { + this._trace.push(htmlGenerator.generateHTML(traceElement)); + }); + this._traceIndex = 0; + const panel = vscode.window.createWebviewPanel( + 'programflow-visualization', + 'Code Visualization', // TODO adjust name to original file name + vscode.ViewColumn.Beside, + { + enableScripts: true, + localResourceRoots: [vscode.Uri.file(path.join(context.extensionPath, FRONTEND_RESOURCE_PATH))], + } + ); + + // Get path to resource on disk + const stylesFile = vscode.Uri.file(path.join(context.extensionPath, FRONTEND_RESOURCE_PATH, 'webview.css')); + const scriptFile = vscode.Uri.file(path.join(context.extensionPath, FRONTEND_RESOURCE_PATH, 'webview.js')); + const lineFile = vscode.Uri.file(path.join(context.extensionPath, FRONTEND_RESOURCE_PATH, 'leader-line.min.js')); + // And get the special URI to use with the webview + this._style = panel.webview.asWebviewUri(stylesFile); + this._script = panel.webview.asWebviewUri(scriptFile); + this._lineScript = panel.webview.asWebviewUri(lineFile); + this._panel = panel; + + this._panel.onDidChangeViewState(async (e) => { + if (e.webviewPanel.active) { + await this.postMessagesToWebview('updateContent'); + } + }); + + this._tracePortSelfClose = false; + this._panel.onDidDispose( + async () => { + this._tracePortSelfClose = true; + this._tracePort?.close(); + this.updateLineHighlight(true); + this._panel = undefined; + }, + null, + context.subscriptions + ); + + vscode.window.onDidChangeActiveTextEditor(_ => { + if (this._panel?.active) { + this.updateLineHighlight(); + } + }, undefined, context.subscriptions); + + + // Message Receivers + this._panel.webview.onDidReceiveMessage( + (msg) => { + switch (msg.command) { + case 'onClick': + return this.onClick(msg.type); + case 'onSlide': + return this.onSlide(msg.sliderValue); + } + }, + undefined, + context.subscriptions + ); + + this.updateLineHighlight(); + this.initWebviewContent(); + + this._tracePort?.on('message', async (backendTraceElem) => { + const firstElement = this._trace.length === 0; + this._backendTrace.trace.push(backendTraceElem); + this._trace.push((new HTMLGenerator()).generateHTML(backendTraceElem)); + await this.postMessagesToWebview('updateButtons', 'updateContent'); + if (firstElement) { + this.updateLineHighlight(); + } + }); + this._tracePort?.on('close', async () => { + if (!this._tracePortSelfClose) { + this._backendTrace.complete = true; + await cacheTrace(context, this._fileHash, this._backendTrace.trace); + await this.postMessagesToWebview('updateContent'); + } + }); + } + + public static async getVisualizationPanel( + context: vscode.ExtensionContext, + fileHash: string, + trace: BackendTrace, + tracePort: MessagePort | null + ): Promise { + return new VisualizationPanel(context, fileHash, trace, tracePort); + } + + // TODO: Look if Typescript is possible OR do better documentation in all files + public initWebviewContent() { + this._panel!.webview.html = ` + + + + + + + + + Code Visualization + + +
+
+
+ Frames +
+
+
+ Objects +
+
+
+
+
+
+
+
+
+
+
+ +
+
+

Step 

+

0

+

/?

+
+
+ + + + +
+ + + `; + } + + private async updateLineHighlight(remove: boolean = false) { + if (this._trace.length === 0) { + return; + } + let editor: vscode.TextEditor | undefined = vscode.window.visibleTextEditors.filter( + editor => path.basename(editor.document.uri.path) === path.basename(this._trace[this._traceIndex][3]!) + )[0]; + + const openPath = vscode.Uri.parse(this._trace[this._traceIndex][3]!); + if (!editor || editor.document.uri.path !== openPath.path) { + await vscode.commands.executeCommand('workbench.action.focusFirstEditorGroup'); + const document = await vscode.workspace.openTextDocument(openPath); + editor = await vscode.window.showTextDocument(document); + } + + if (remove) { + editor.setDecorations(nextLineExecuteHighlightType, []); + } else { + this.setNextLineHighlighting(editor); + } + } + + private setNextLineHighlighting(editor: vscode.TextEditor) { + if (this._trace.length === 0) { + return; + } + const nextLine = this._traceIndex !== this._trace.length - 1 ? this._trace[this._traceIndex][0] - 1 : -1; + + if (nextLine > -1) { + this.setEditorDecorations(editor, nextLineExecuteHighlightType, nextLine); + } + } + + private setEditorDecorations(editor: vscode.TextEditor, highlightType: vscode.TextEditorDecorationType, line: number) { + editor.setDecorations( + highlightType, + this.createDecorationOptions( + new vscode.Range(new vscode.Position(line, 0), new vscode.Position(line, 999)) + ) + ); + } + + private async onClick(type: string) { + this.updateTraceIndex(type); + await this.postMessagesToWebview('updateButtons', 'updateContent'); + this.updateLineHighlight(); + } + + private async onSlide(sliderValue: number) { + this._traceIndex = Number(sliderValue); + await this.postMessagesToWebview('updateButtons', 'updateContent'); + this.updateLineHighlight(); + } + + private updateTraceIndex(actionType: string) { + switch (actionType) { + case 'next': ++this._traceIndex; + break; + case 'prev': --this._traceIndex; + break; + case 'first': this._traceIndex = 0; + break; + case 'last': this._traceIndex = this._trace.length - 1; + break; + default: + break; + } + } + + private async postMessagesToWebview(...args: string[]) { + for (const message of args) { + switch (message) { + case 'updateButtons': + const nextActive = this._traceIndex < this._trace.length - 1; + const prevActive = this._traceIndex > 0; + const firstActive = this._traceIndex > 0; + const lastActive = this._traceIndex !== this._trace.length - 1; + await this._panel!.webview.postMessage({ + command: 'updateButtons', + next: nextActive, + prev: prevActive, + first: firstActive, + last: lastActive, + }); + break; + case 'updateContent': + await this._panel!.webview.postMessage({ + command: 'updateContent', + traceComplete: this._backendTrace.complete, + traceElem: this._trace[this._traceIndex], + traceIndex: this._traceIndex, + traceLen: this._trace.length, + }); + break; + } + }; + } + + private createDecorationOptions(range: vscode.Range): vscode.DecorationOptions[] { + return [ + { + range: range, + }, + ]; + } +} diff --git a/src/programflow-visualization/main.ts b/src/programflow-visualization/main.ts new file mode 100644 index 00000000..5f98b13d --- /dev/null +++ b/src/programflow-visualization/main.ts @@ -0,0 +1,45 @@ +import * as vscode from 'vscode'; +import { startBackend } from './backend/backend'; +import * as FileHandler from './FileHandler'; +import { Md5 } from 'ts-md5'; +import { startFrontend } from './frontend/frontend'; +import { traceAlreadyExists } from './trace_cache'; + +export function getProgFlowVizCallback(context: vscode.ExtensionContext): () => Promise { + return async () => { + try { + const file = vscode.window.activeTextEditor ? + vscode.window.activeTextEditor.document.uri : + undefined; + if (!file) { + vscode.window.showWarningMessage('No file is open'); + return; + } + if (!file.fsPath.endsWith('.py')) { + vscode.window.showWarningMessage('Not a Python file'); + return; + } + vscode.window.activeTextEditor?.document.save(); + + const content = await FileHandler.getContentOf(file); + const fileHash = Md5.hashStr(content); + + let tracePort = null; + if (!(await traceAlreadyExists(context, fileHash))) { + tracePort = startBackend(context, file); + } + + const result = await startFrontend(context, fileHash, tracePort); + if (result) { + await vscode.window.showErrorMessage("Error ProgramFlow-Visualization: " + result.errorMessage); + return; + } + } catch (e: any) { + if (e instanceof Error) { + console.log(e.stack?.toString()); + } else { + console.log(e); + } + } + }; +} diff --git a/src/programflow-visualization/trace_cache.ts b/src/programflow-visualization/trace_cache.ts new file mode 100644 index 00000000..0cc049d8 --- /dev/null +++ b/src/programflow-visualization/trace_cache.ts @@ -0,0 +1,49 @@ +import { Variables } from './constants'; +import * as vscode from 'vscode'; +import * as tmp from 'tmp'; +import path = require('path'); +import stringify from 'stringify-json'; +import util = require('util'); +import * as FileHandler from './FileHandler'; + +export async function initTraceCache(context: vscode.ExtensionContext): Promise { + tmp.setGracefulCleanup(); + const tmpDir = tmp.dirSync({ prefix: 'ProgramFlowVisualization', unsafeCleanup: true }); + await setContextState(context, Variables.TRACE_CACHE_DIR, tmpDir.name); + + const cachedTraces: string[] = []; + await setContextState(context, Variables.CACHED_TRACES, cachedTraces); + +} + +export async function traceAlreadyExists(context: vscode.ExtensionContext, fileHash: string): Promise { + // Check if the hash is contained in cachedTraces, a list of hashes for which a trace already exists + const cachedTraces: string[] = await getContextState(context, Variables.CACHED_TRACES) ?? []; + return cachedTraces.includes(fileHash); +} + +async function getContextState(context: vscode.ExtensionContext, key: string): Promise { + return await context.workspaceState.get(key); +} + +async function setContextState(context: vscode.ExtensionContext, key: string, value: any): Promise { + return await context.workspaceState.update(key, value); +} + +async function traceCachePath(context: vscode.ExtensionContext, fileHash: string): Promise { + const cacheDir = await getContextState(context, Variables.TRACE_CACHE_DIR) ?? ""; + return path.join(cacheDir, fileHash + '.json'); +} + +export async function cacheTrace(context: vscode.ExtensionContext, fileHash: string, trace: BackendTrace) { + const path = await traceCachePath(context, fileHash); + await vscode.workspace.fs.writeFile(vscode.Uri.file(path), new util.TextEncoder().encode(stringify(trace))); + + const cachedTraces: string[] = await getContextState(context, Variables.CACHED_TRACES) ?? []; + cachedTraces.push(fileHash); +} + +export async function getTrace(context: vscode.ExtensionContext, fileHash: string): Promise { + const path = await traceCachePath(context, fileHash); + return JSON.parse(await FileHandler.getContentOf(vscode.Uri.file(path))); +} diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts new file mode 100644 index 00000000..b91a452e --- /dev/null +++ b/src/programflow-visualization/types.ts @@ -0,0 +1,52 @@ +/** + * For better readable code +*/ +type Try = Success | Failure; +type Success = { result: any }; +type Failure = { errorMessage: string }; + +// State Types for the Frontend +type FrontendTrace = Array; +type FrontendTraceElem = [number, string, string, string]; +// ############################################################################################ +// State Types for the Backend +type PartialBackendTrace = { + trace: BackendTrace; + complete: boolean; +}; +type BackendTrace = Array; +type BackendTraceElem = { + line: number; + filePath: string, + stack: Array; + heap: Map; +}; + +type Address = number; + +type Value = + | { type: 'int'; value: number } + | { type: 'float'; value: number } + | { type: 'str'; value: string } + | { type: 'none'; value: string } + | { type: 'bool'; value: string } + | { type: 'ref'; value: Address }; + +type NamedValue = Value & { + name: string; +}; + + +type StackElem = { + frameName: string; + locals: Array; +}; + +type HeapValue = + | { type: 'list'; value: Array } + | { type: 'tuple'; value: Array } + | { type: 'set'; value: Array } + | { type: 'dict'; keys: Map, value: Map } + | { type: 'instance'; name: string, value: Map }; +// wrapper type -> frontend list elements dodge + From 9c1b3f42a81c77cde7fbd37b9fe8ee62813000fa Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 25 Sep 2024 21:33:10 +0200 Subject: [PATCH 02/43] Add a few trace generator tests --- .../test/generateExpectation.py | 19 ++ .../deps/pytrace-generator/test/runTests.py | 38 ++++ .../test/test-cases/empty.py | 0 .../test/test-cases/empty.py.json | 13 ++ .../test/test-cases/factorial.py | 8 + .../test/test-cases/factorial.py.json | 202 ++++++++++++++++++ .../test/test-cases/generatorObject.py | 1 + .../test/test-cases/importArgparse.py | 1 + .../test/test-cases/importArgparse.py.json | 13 ++ .../test/test-cases/importShutil.py | 1 + .../test/test-cases/importShutil.py.json | 13 ++ .../test/test-cases/importSubprocess.py | 1 + .../test/test-cases/importSubprocess.py.json | 13 ++ .../test/test-cases/importTime.py | 1 + .../test/test-cases/importTime.py.json | 13 ++ python/deps/pytrace-generator/test/util.py | 16 ++ 16 files changed, 353 insertions(+) create mode 100644 python/deps/pytrace-generator/test/generateExpectation.py create mode 100644 python/deps/pytrace-generator/test/runTests.py create mode 100644 python/deps/pytrace-generator/test/test-cases/empty.py create mode 100644 python/deps/pytrace-generator/test/test-cases/empty.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/factorial.py create mode 100644 python/deps/pytrace-generator/test/test-cases/factorial.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/generatorObject.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importArgparse.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importArgparse.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/importShutil.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importShutil.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/importSubprocess.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/importTime.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importTime.py.json create mode 100644 python/deps/pytrace-generator/test/util.py diff --git a/python/deps/pytrace-generator/test/generateExpectation.py b/python/deps/pytrace-generator/test/generateExpectation.py new file mode 100644 index 00000000..85d3433d --- /dev/null +++ b/python/deps/pytrace-generator/test/generateExpectation.py @@ -0,0 +1,19 @@ +import json +import os +import subprocess +import sys + +from util import read_trace_entries + +if __name__ == '__main__': + python_file = sys.argv[1] + trace_file = f"{sys.argv[1]}.json" + p = subprocess.run( + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file], + capture_output=True + ) + if p.returncode != 0: + raise RuntimeError("trace generator did not exit with exit code 0") + trace_entries = read_trace_entries(p.stdout) + with open(trace_file, "w") as f: + json.dump(trace_entries, f, indent="\t") diff --git a/python/deps/pytrace-generator/test/runTests.py b/python/deps/pytrace-generator/test/runTests.py new file mode 100644 index 00000000..dde75d6e --- /dev/null +++ b/python/deps/pytrace-generator/test/runTests.py @@ -0,0 +1,38 @@ +import json +import os +import subprocess +import unittest + +from util import read_trace_entries + +class TestTraceGeneration(unittest.TestCase): + pass + +def generate_test(python_file, expected_trace_path): + def test(self): + p = subprocess.run( + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file], + capture_output=True + ) + self.assertEqual(p.returncode, 0) + if expected_trace_path is not None: + trace_entries = read_trace_entries(p.stdout) + with open(expected_trace_path) as f: + expected_trace = json.load(f) + self.assertEqual(trace_entries, expected_trace) + + return test + +if __name__ == '__main__': + test_cases_dir = os.path.join(os.path.dirname(__file__), "test-cases") + with os.scandir(test_cases_dir) as scan: + for entry in scan: + if not entry.is_file() or not entry.name.endswith(".py"): + continue + json_path = f"{entry.path}.json" + if not os.path.isfile(json_path): + json_path = None + test_name = f'test_{entry.name[:-3]}' + test = generate_test(entry.path, json_path) + setattr(TestTraceGeneration, test_name, test) + unittest.main() diff --git a/python/deps/pytrace-generator/test/test-cases/empty.py b/python/deps/pytrace-generator/test/test-cases/empty.py new file mode 100644 index 00000000..e69de29b diff --git a/python/deps/pytrace-generator/test/test-cases/empty.py.json b/python/deps/pytrace-generator/test/test-cases/empty.py.json new file mode 100644 index 00000000..573e9b11 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/empty.py.json @@ -0,0 +1,13 @@ +[ + { + "line": 2, + "filePath": "empty.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py b/python/deps/pytrace-generator/test/test-cases/factorial.py new file mode 100644 index 00000000..b3245fc0 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/factorial.py @@ -0,0 +1,8 @@ +def factorical(n): + if n <= 1: + return 1 + prev = factorical(n - 1) + a = n * prev + return a + +res1 = factorical(2) diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py.json b/python/deps/pytrace-generator/test/test-cases/factorial.py.json new file mode 100644 index 00000000..c34d7ca6 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/factorial.py.json @@ -0,0 +1,202 @@ +[ + { + "line": 1, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 8, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 2, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + } + ] + } + ], + "heap": {} + }, + { + "line": 4, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + } + ] + } + ], + "heap": {} + }, + { + "line": 2, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + } + ] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 1, + "name": "n" + } + ] + } + ], + "heap": {} + }, + { + "line": 3, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + } + ] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 1, + "name": "n" + } + ] + } + ], + "heap": {} + }, + { + "line": 5, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + }, + { + "type": "int", + "value": 1, + "name": "prev" + } + ] + } + ], + "heap": {} + }, + { + "line": 6, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + }, + { + "type": "int", + "value": 1, + "name": "prev" + }, + { + "type": "int", + "value": 2, + "name": "a" + } + ] + } + ], + "heap": {} + }, + { + "line": 10, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "int", + "value": 2, + "name": "res1" + } + ] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/generatorObject.py b/python/deps/pytrace-generator/test/test-cases/generatorObject.py new file mode 100644 index 00000000..1a51203e --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/generatorObject.py @@ -0,0 +1 @@ +g = (i for i in [1, 2]) \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importArgparse.py b/python/deps/pytrace-generator/test/test-cases/importArgparse.py new file mode 100644 index 00000000..1b647525 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importArgparse.py @@ -0,0 +1 @@ +import argparse diff --git a/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json b/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json new file mode 100644 index 00000000..51992f5e --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json @@ -0,0 +1,13 @@ +[ + { + "line": 3, + "filePath": "importArgparse.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importShutil.py b/python/deps/pytrace-generator/test/test-cases/importShutil.py new file mode 100644 index 00000000..d7b1da23 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importShutil.py @@ -0,0 +1 @@ +import shutil diff --git a/python/deps/pytrace-generator/test/test-cases/importShutil.py.json b/python/deps/pytrace-generator/test/test-cases/importShutil.py.json new file mode 100644 index 00000000..9f952a4b --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importShutil.py.json @@ -0,0 +1,13 @@ +[ + { + "line": 3, + "filePath": "importShutil.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py new file mode 100644 index 00000000..2933cb5d --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py @@ -0,0 +1 @@ +import subprocess diff --git a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json new file mode 100644 index 00000000..420bf012 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json @@ -0,0 +1,13 @@ +[ + { + "line": 3, + "filePath": "importSubprocess.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py b/python/deps/pytrace-generator/test/test-cases/importTime.py new file mode 100644 index 00000000..bd35e428 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importTime.py @@ -0,0 +1 @@ +import time diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py.json b/python/deps/pytrace-generator/test/test-cases/importTime.py.json new file mode 100644 index 00000000..ff0b975b --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importTime.py.json @@ -0,0 +1,13 @@ +[ + { + "line": 3, + "filePath": "importTime.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/util.py b/python/deps/pytrace-generator/test/util.py new file mode 100644 index 00000000..779dc34e --- /dev/null +++ b/python/deps/pytrace-generator/test/util.py @@ -0,0 +1,16 @@ +import json + +def read_trace_entries(stdout): + entries = [] + while len(stdout) > 0: + entry_len = int.from_bytes(stdout[:4], byteorder="big", signed=False) + stdout = stdout[4:] + entry = json.loads(str(stdout[:entry_len], encoding="utf-8")) + test_cases_str = "test-cases/" + idx = entry["filePath"].rfind(test_cases_str) + if idx >= 0: + # Remove user specific path stuff to allow for equality testing + entry["filePath"] = entry["filePath"][idx + len(test_cases_str):] + entries.append(entry) + stdout = stdout[entry_len:] + return entries From 763b704f83bb5d1ab441f46d5c498b71128dab27 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Fri, 27 Sep 2024 12:50:37 +0200 Subject: [PATCH 03/43] bump versions of several dependencies --- package-lock.json | 811 ++++++++++++++++++++++++---------------- package.json | 9 +- src/test/runTest.ts | 2 +- src/test/suite/index.ts | 35 +- 4 files changed, 513 insertions(+), 344 deletions(-) diff --git a/package-lock.json b/package-lock.json index 2b92642c..20ca8d0d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -16,18 +16,17 @@ "ts-md5": "^1.3.1" }, "devDependencies": { - "@types/glob": "^7.1.4", "@types/mocha": "^8.2.3", "@types/node": "^18.11.13", "@types/tmp": "^0.2.6", "@types/vscode": "^1.85.0", "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.30.0", + "@vscode/test-electron": "^1.6.1", "eslint": "^8.18.0", - "glob": "^7.2.0", - "mocha": "^8.4.0", - "typescript": "^4.7.4", - "vscode-test": "^1.6.1" + "glob": "^11.0.0", + "mocha": "^10.7.3", + "typescript": "^5.4.2" }, "engines": { "vscode": "^1.85.0" @@ -157,6 +156,102 @@ "dev": true, "license": "BSD-3-Clause" }, + "node_modules/@isaacs/cliui": { + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/@isaacs/cliui/-/cliui-8.0.2.tgz", + "integrity": "sha512-O8jcjabXaleOG9DQ0+ARXWZBTfnP4WNAqzuiJK7ll44AmxGKv/J2M4TPjxjY3znBCfvBXFzucm1twdyFybFqEA==", + "dev": true, + "dependencies": { + "string-width": "^5.1.2", + "string-width-cjs": "npm:string-width@^4.2.0", + "strip-ansi": "^7.0.1", + "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", + "wrap-ansi": "^8.1.0", + "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-regex": { + "version": "6.1.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.1.0.tgz", + "integrity": "sha512-7HSX4QQb4CspciLpVFwyRe79O3xsIZDDLER21kERQ71oaPodF8jL725AgJMFAYbooIqolJoRLuM81SpeUkpkvA==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/ansi-styles": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", + "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/emoji-regex": { + "version": "9.2.2", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", + "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", + "dev": true + }, + "node_modules/@isaacs/cliui/node_modules/string-width": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-5.1.2.tgz", + "integrity": "sha512-HnLOCR3vjcY8beoNLtcjZ5/nxn2afmME6lhrDrebokqMap+XbeW8n9TXpPDOqdGK5qcI3oT0GKTW6wC7EMiVqA==", + "dev": true, + "dependencies": { + "eastasianwidth": "^0.2.0", + "emoji-regex": "^9.2.2", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@isaacs/cliui/node_modules/strip-ansi": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", + "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { + "version": "8.1.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", + "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^6.1.0", + "string-width": "^5.0.1", + "strip-ansi": "^7.0.1" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" + } + }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", @@ -204,16 +299,6 @@ "node": ">= 6" } }, - "node_modules/@types/glob": { - "version": "7.1.4", - "resolved": "https://registry.npmjs.org/@types/glob/-/glob-7.1.4.tgz", - "integrity": "sha512-w+LsMxKyYQm347Otw+IfBXOv9UWVjpHpCDdbBMt8Kz/xbvCYNjP+0qPh91Km3iKfSRLBB0P7fAMf0KHrPu+MyA==", - "dev": true, - "dependencies": { - "@types/minimatch": "*", - "@types/node": "*" - } - }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", @@ -221,12 +306,6 @@ "dev": true, "license": "MIT" }, - "node_modules/@types/minimatch": { - "version": "3.0.5", - "resolved": "https://registry.npmjs.org/@types/minimatch/-/minimatch-3.0.5.tgz", - "integrity": "sha512-Klz949h02Gz2uZCMGwDUSDS1YBlTdDDgbWHi+81l29tQALUtvz4rAYi5uoVhE5Lagoq6DeqAUlbrHvW/mXDgdQ==", - "dev": true - }, "node_modules/@types/mocha": { "version": "8.2.3", "resolved": "https://registry.npmjs.org/@types/mocha/-/mocha-8.2.3.tgz", @@ -460,12 +539,6 @@ "url": "https://opencollective.com/typescript-eslint" } }, - "node_modules/@ungap/promise-all-settled": { - "version": "1.1.2", - "resolved": "https://registry.npmjs.org/@ungap/promise-all-settled/-/promise-all-settled-1.1.2.tgz", - "integrity": "sha512-sL/cEvJWAnClXw0wHk85/2L0G6Sj8UB0Ctc1TEMbKSsmpRosqhwj9gWgFRZSrBr2f9tiXISwNhCPmlfqUqyb9Q==", - "dev": true - }, "node_modules/@ungap/structured-clone": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.2.0.tgz", @@ -473,6 +546,21 @@ "dev": true, "license": "ISC" }, + "node_modules/@vscode/test-electron": { + "version": "1.6.2", + "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-1.6.2.tgz", + "integrity": "sha512-W01ajJEMx6223Y7J5yaajGjVs1QfW3YGkkOJHVKfAMEqNB1ZHN9wCcViehv5ZwVSSJnjhu6lYEYgwBdHtCxqhQ==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^4.0.1", + "https-proxy-agent": "^5.0.0", + "rimraf": "^3.0.2", + "unzipper": "^0.10.11" + }, + "engines": { + "node": ">=8.9.3" + } + }, "node_modules/acorn": { "version": "8.12.1", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.12.1.tgz", @@ -526,9 +614,9 @@ } }, "node_modules/ansi-colors": { - "version": "4.1.1", - "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.1.tgz", - "integrity": "sha512-JoX0apGbHaUJBNl6yF+p6JAFYZ666/hhCGKN5t9QFjbJQKUU/g8MNbFDbvfrgKXvI1QpZplPOnwIo99lX/AAmA==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/ansi-colors/-/ansi-colors-4.1.3.tgz", + "integrity": "sha512-/6w/C21Pm1A7aZitlI5Ni/2J6FFQN8i1Cvz3kHABAAbw93v/NlvKdVOqz7CCWz/3iv/JplRSEEZ83XION15ovw==", "dev": true, "engines": { "node": ">=6" @@ -543,10 +631,25 @@ "node": ">=8" } }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, "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, "dependencies": { "normalize-path": "^3.0.0", @@ -602,12 +705,15 @@ } }, "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, "engines": { "node": ">=8" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/bluebird": { @@ -713,39 +819,6 @@ "url": "https://github.com/chalk/chalk?sponsor=1" } }, - "node_modules/chalk/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/chalk/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/chalk/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/chalk/node_modules/has-flag": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", @@ -768,24 +841,27 @@ } }, "node_modules/chokidar": { - "version": "3.5.1", - "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.5.1.tgz", - "integrity": "sha512-9+s+Od+W0VJJzawDma/gvBNQqkTiqYTWLuZoyAsivsI4AaWTCzHG06/TMjsf1cYe9Cb97UCEhjz7HvnPk2p/tw==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/chokidar/-/chokidar-3.6.0.tgz", + "integrity": "sha512-7VT13fmjotKpGipCW9JEQAusEPE+Ei8nl6/g4FBAmIm0GOOLMua9NDDo/DWp0ZAxCr3cPq5ZpBqmPAQgDda2Pw==", "dev": true, "dependencies": { - "anymatch": "~3.1.1", + "anymatch": "~3.1.2", "braces": "~3.0.2", - "glob-parent": "~5.1.0", + "glob-parent": "~5.1.2", "is-binary-path": "~2.1.0", "is-glob": "~4.0.1", "normalize-path": "~3.0.0", - "readdirp": "~3.5.0" + "readdirp": "~3.6.0" }, "engines": { "node": ">= 8.10.0" }, + "funding": { + "url": "https://paulmillr.com/funding/" + }, "optionalDependencies": { - "fsevents": "~2.3.1" + "fsevents": "~2.3.2" } }, "node_modules/cliui": { @@ -799,6 +875,24 @@ "wrap-ansi": "^7.0.0" } }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", @@ -864,9 +958,9 @@ "license": "MIT" }, "node_modules/diff": { - "version": "5.0.0", - "resolved": "https://registry.npmjs.org/diff/-/diff-5.0.0.tgz", - "integrity": "sha512-/VTCrvm5Z0JGty/BWHljh+BAiw3IK+2j87NGMu8Nwc/f48WoDAC395uomO9ZD117ZOBaHmkX1oyLvkVM/aIT3w==", + "version": "5.2.0", + "resolved": "https://registry.npmjs.org/diff/-/diff-5.2.0.tgz", + "integrity": "sha512-uIFDxqpRZGZ6ThOk84hEfqWoHx2devRFvpTZcTHur85vImfaxUbTW9Ryh4CpCuDnToOP1CEtXKIgytHBPVff5A==", "dev": true, "engines": { "node": ">=0.3.1" @@ -907,6 +1001,12 @@ "readable-stream": "^2.0.2" } }, + "node_modules/eastasianwidth": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", + "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", + "dev": true + }, "node_modules/emoji-regex": { "version": "8.0.0", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", @@ -1275,6 +1375,22 @@ "dev": true, "license": "ISC" }, + "node_modules/foreground-child": { + "version": "3.3.0", + "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.3.0.tgz", + "integrity": "sha512-Ld2g8rrAyMYFXBhEqMz8ZAHBi4J4uS1i/CxGMDnjyFWddMXLVcDp051DZfu+t7+ab7Wv6SMqpWmyFIj5UbfFvg==", + "dev": true, + "dependencies": { + "cross-spawn": "^7.0.0", + "signal-exit": "^4.0.1" + }, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", @@ -1282,9 +1398,9 @@ "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, "optional": true, @@ -1311,6 +1427,39 @@ "node": ">=0.6" } }, + "node_modules/fstream/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/fstream/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/fstream/node_modules/rimraf": { "version": "2.7.1", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-2.7.1.tgz", @@ -1334,21 +1483,23 @@ } }, "node_modules/glob": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.0.tgz", - "integrity": "sha512-lmLf6gtyrPq8tTjSmrO94wBeQbFR3HbLHbuyD69wuyQkImp2hWqMGB47OX65FBkPffO641IP9jWa1z4ivqG26Q==", - "deprecated": "Glob versions prior to v9 are no longer supported", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "foreground-child": "^3.1.0", + "jackspeak": "^4.0.1", + "minimatch": "^10.0.0", + "minipass": "^7.1.2", + "package-json-from-dist": "^1.0.0", + "path-scurry": "^2.0.0" + }, + "bin": { + "glob": "dist/esm/bin.mjs" }, "engines": { - "node": "*" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1366,6 +1517,30 @@ "node": ">= 6" } }, + "node_modules/glob/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" + } + }, + "node_modules/glob/node_modules/minimatch": { + "version": "10.0.1", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.0.1.tgz", + "integrity": "sha512-ethXTt3SGGR+95gudmqJ1eNhRO7eGEGIgYA9vnPatK4/etz2MEVDno5GMCibdMTuBMyElzIlgxMna3K94XDIDQ==", + "dev": true, + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", @@ -1416,15 +1591,6 @@ "dev": true, "license": "MIT" }, - "node_modules/growl": { - "version": "1.10.5", - "resolved": "https://registry.npmjs.org/growl/-/growl-1.10.5.tgz", - "integrity": "sha512-qBr4OuELkhPenW6goKVXiv47US3clb3/IbuWF9KNKEijAy9oeHxU9IgzjvJhHkUzhaj7rOUD7+YGWqUjLp5oSA==", - "dev": true, - "engines": { - "node": ">=4.x" - } - }, "node_modules/he": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/he/-/he-1.2.0.tgz", @@ -1587,6 +1753,18 @@ "node": ">=8" } }, + "node_modules/is-unicode-supported": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/is-unicode-supported/-/is-unicode-supported-0.1.0.tgz", + "integrity": "sha512-knxG2q4UC3u8stRGyAVJCOdxFmv5DZiRcdlIaAQXAbSfJya+OhopNotLQrstBhququ4ZpuKbDc/8S6mgXgPFPw==", + "dev": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, "node_modules/isarray": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/isarray/-/isarray-1.0.0.tgz", @@ -1599,6 +1777,21 @@ "integrity": "sha1-6PvzdNxVb/iUehDcsFctYz8s+hA=", "dev": true }, + "node_modules/jackspeak": { + "version": "4.0.2", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.2.tgz", + "integrity": "sha512-bZsjR/iRjl1Nk1UkjGpAzLNfQtzuijhn2g+pbZb98HQ1Gk8vM9hfbxeMBP+M2/UUdwj0RqGG3mlvk2MsAqwvEw==", + "dev": true, + "dependencies": { + "@isaacs/cliui": "^8.0.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/js-yaml": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.0.tgz", @@ -1692,15 +1885,28 @@ "license": "MIT" }, "node_modules/log-symbols": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.0.0.tgz", - "integrity": "sha512-FN8JBzLx6CzeMrB0tg6pqlGU1wCrXW+ZXGH481kfsBqer0hToTIiHdjH4Mq8xJUbvATujKCvaREGWpGUionraA==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/log-symbols/-/log-symbols-4.1.0.tgz", + "integrity": "sha512-8XPvpAA8uyhfteu8pIvQxpJZ7SYYdpUivZpGy6sFsBuKRY/7rQGavedeB8aK+Zkyq6upMFVL/9AW6vOYzfRyLg==", "dev": true, "dependencies": { - "chalk": "^4.0.0" + "chalk": "^4.1.0", + "is-unicode-supported": "^0.1.0" }, "engines": { "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/lru-cache": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", + "dev": true, + "engines": { + "node": "20 || >=22" } }, "node_modules/map2object": { @@ -1734,22 +1940,43 @@ } }, "node_modules/minimatch": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.0.4.tgz", - "integrity": "sha512-yJHVQEhyqPLUTgt9B83PXu6W3rx4MvvHvSUvToogpwoGDOUQ+yDrR0HRot+yOCdCO7u4hX3pWft6kWBBcqh0UA==", + "version": "5.1.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", + "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { - "brace-expansion": "^1.1.7" + "brace-expansion": "^2.0.1" }, "engines": { - "node": "*" + "node": ">=10" + } + }, + "node_modules/minimatch/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==", + "dev": true, + "dependencies": { + "balanced-match": "^1.0.0" } }, "node_modules/minimist": { - "version": "1.2.5", - "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.5.tgz", - "integrity": "sha512-FM9nNUYrRBAELZQT3xeZQ7fmMOBg6nWNmJKTcgsJeaLstP/UODVpGsr5OhXhhXg6f+qtJ8uiZ+PUxkDWcgIXLw==", - "dev": true + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } }, "node_modules/mkdirp": { "version": "0.5.5", @@ -1764,88 +1991,55 @@ } }, "node_modules/mocha": { - "version": "8.4.0", - "resolved": "https://registry.npmjs.org/mocha/-/mocha-8.4.0.tgz", - "integrity": "sha512-hJaO0mwDXmZS4ghXsvPVriOhsxQ7ofcpQdm8dE+jISUOKopitvnXFQmpRR7jd2K6VBG6E26gU3IAbXXGIbu4sQ==", - "dev": true, - "dependencies": { - "@ungap/promise-all-settled": "1.1.2", - "ansi-colors": "4.1.1", - "browser-stdout": "1.3.1", - "chokidar": "3.5.1", - "debug": "4.3.1", - "diff": "5.0.0", - "escape-string-regexp": "4.0.0", - "find-up": "5.0.0", - "glob": "7.1.6", - "growl": "1.10.5", - "he": "1.2.0", - "js-yaml": "4.0.0", - "log-symbols": "4.0.0", - "minimatch": "3.0.4", - "ms": "2.1.3", - "nanoid": "3.1.20", - "serialize-javascript": "5.0.1", - "strip-json-comments": "3.1.1", - "supports-color": "8.1.1", - "which": "2.0.2", - "wide-align": "1.1.3", - "workerpool": "6.1.0", - "yargs": "16.2.0", - "yargs-parser": "20.2.4", - "yargs-unparser": "2.0.0" + "version": "10.7.3", + "resolved": "https://registry.npmjs.org/mocha/-/mocha-10.7.3.tgz", + "integrity": "sha512-uQWxAu44wwiACGqjbPYmjo7Lg8sFrS3dQe7PP2FQI+woptP4vZXSMcfMyFL/e1yFEeEpV4RtyTpZROOKmxis+A==", + "dev": true, + "dependencies": { + "ansi-colors": "^4.1.3", + "browser-stdout": "^1.3.1", + "chokidar": "^3.5.3", + "debug": "^4.3.5", + "diff": "^5.2.0", + "escape-string-regexp": "^4.0.0", + "find-up": "^5.0.0", + "glob": "^8.1.0", + "he": "^1.2.0", + "js-yaml": "^4.1.0", + "log-symbols": "^4.1.0", + "minimatch": "^5.1.6", + "ms": "^2.1.3", + "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", + "yargs-unparser": "^2.0.0" }, "bin": { "_mocha": "bin/_mocha", - "mocha": "bin/mocha" - }, - "engines": { - "node": ">= 10.12.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/mochajs" - } - }, - "node_modules/mocha/node_modules/debug": { - "version": "4.3.1", - "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.1.tgz", - "integrity": "sha512-doEwdvm4PCeK4K3RQN2ZC2BYUBaxwLARCqZmMjtF8a51J2Rb0xpVloFRnCODwqjpwnAoao4pelN8l3RJdv3gRQ==", - "dev": true, - "dependencies": { - "ms": "2.1.2" + "mocha": "bin/mocha.js" }, "engines": { - "node": ">=6.0" - }, - "peerDependenciesMeta": { - "supports-color": { - "optional": true - } + "node": ">= 14.0.0" } }, - "node_modules/mocha/node_modules/debug/node_modules/ms": { - "version": "2.1.2", - "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", - "integrity": "sha512-sGkPx+VjMtmA6MX27oA4FBFELFCZZ4S4XqeGOXCv68tT+jb3vk/RyaKWP0PTKyWtmLSM0b+adUTEvbs1PEaH2w==", - "dev": true - }, "node_modules/mocha/node_modules/glob": { - "version": "7.1.6", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.6.tgz", - "integrity": "sha512-LwaxwyZ72Lk7vZINtNNrywX0ZuLyStrdDtabefZKAY5ZGJhVtgdznluResxNmPitE0SAO+O26sWTHeKSI2wMBA==", + "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", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" + "minimatch": "^5.0.1", + "once": "^1.3.0" }, "engines": { - "node": "*" + "node": ">=12" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -1860,18 +2054,6 @@ "node": ">=8" } }, - "node_modules/mocha/node_modules/js-yaml": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.0.0.tgz", - "integrity": "sha512-pqon0s+4ScYUvX30wxQi3PogGFAlUyH0awepWvwkj4jD4v+ova3RiYw8bmA6x2rDrEaj8i/oWKoRxpVNW+Re8Q==", - "dev": true, - "dependencies": { - "argparse": "^2.0.1" - }, - "bin": { - "js-yaml": "bin/js-yaml.js" - } - }, "node_modules/mocha/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", @@ -1894,18 +2076,6 @@ "dev": true, "license": "MIT" }, - "node_modules/nanoid": { - "version": "3.1.20", - "resolved": "https://registry.npmjs.org/nanoid/-/nanoid-3.1.20.tgz", - "integrity": "sha512-a1cQNyczgKbLX9jwbS/+d7W8fX/RfgYR7lVWwWOGIPNgK2m0MWvrGF6/m4kk6U3QcFMnZf3RIhL0v2Jgh/0Uxw==", - "dev": true, - "bin": { - "nanoid": "bin/nanoid.cjs" - }, - "engines": { - "node": "^10 || ^12 || ^13.7 || ^14 || >=15.0.1" - } - }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", @@ -1986,6 +2156,12 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.1.tgz", + "integrity": "sha512-UEZIS3/by4OC8vL3P2dTXRETpebLI2NiI5vIrjaD/5UtrkFX/tNbwjTSRAGC/+7CAo2pIcBaRgWmcBBHcsaCIw==", + "dev": true + }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", @@ -2027,6 +2203,22 @@ "node": ">=8" } }, + "node_modules/path-scurry": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", @@ -2128,9 +2320,9 @@ "dev": true }, "node_modules/readdirp": { - "version": "3.5.0", - "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.5.0.tgz", - "integrity": "sha512-cMhu7c/8rdhkHXWsY+osBhfSy0JikwpHK/5+imo+LpeasTF8ouErHrlYkwT0++njiyuDvc7OFY5T3ukvZ8qmFQ==", + "version": "3.6.0", + "resolved": "https://registry.npmjs.org/readdirp/-/readdirp-3.6.0.tgz", + "integrity": "sha512-hOS089on8RduqdbhvQ5Z37A0ESjsqz6qnRcffsMU3495FuTdqSm+7bhJ29JvIOsBDEEnan5DPu9t3To9VRlMzA==", "dev": true, "dependencies": { "picomatch": "^2.2.1" @@ -2185,6 +2377,39 @@ "url": "https://github.com/sponsors/isaacs" } }, + "node_modules/rimraf/node_modules/glob": { + "version": "7.2.3", + "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", + "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", + "deprecated": "Glob versions prior to v9 are no longer supported", + "dev": true, + "dependencies": { + "fs.realpath": "^1.0.0", + "inflight": "^1.0.4", + "inherits": "2", + "minimatch": "^3.1.1", + "once": "^1.3.0", + "path-is-absolute": "^1.0.0" + }, + "engines": { + "node": "*" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/node_modules/minimatch": { + "version": "3.1.2", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", + "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", + "dev": true, + "dependencies": { + "brace-expansion": "^1.1.7" + }, + "engines": { + "node": "*" + } + }, "node_modules/run-parallel": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", @@ -2243,9 +2468,9 @@ } }, "node_modules/serialize-javascript": { - "version": "5.0.1", - "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-5.0.1.tgz", - "integrity": "sha512-SaaNal9imEO737H2c05Og0/8LUXG7EnsZyMa8MzkmuHoELfT6txuj0cMqRj6zfPKnmQ1yasR4PCJc8x+M4JSPA==", + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/serialize-javascript/-/serialize-javascript-6.0.2.tgz", + "integrity": "sha512-Saa1xPByTTq2gdeFZYLLo+RFE35NHZkAbqZeWNd3BpzppeVisAqpDjcp8dyf6uIvEqJRd46jemmyA4iFIeVk8g==", "dev": true, "dependencies": { "randombytes": "^2.1.0" @@ -2280,6 +2505,18 @@ "node": ">=8" } }, + "node_modules/signal-exit": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", + "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", + "dev": true, + "engines": { + "node": ">=14" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", @@ -2319,6 +2556,21 @@ "node": ">=8" } }, + "node_modules/string-width-cjs": { + "name": "string-width", + "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.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/stringify-json": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/stringify-json/-/stringify-json-2.0.0.tgz", @@ -2343,6 +2595,19 @@ "node": ">=8" } }, + "node_modules/strip-ansi-cjs": { + "name": "strip-ansi", + "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.1" + }, + "engines": { + "node": ">=8" + } + }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", @@ -2452,17 +2717,16 @@ } }, "node_modules/typescript": { - "version": "4.9.5", - "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", - "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.6.2.tgz", + "integrity": "sha512-NW8ByodCSNCwZeghjN3o+JX5OFH0Ojg6sadjEKY4huZ52TqbJTJnDo5+Tw98lSy63NZvi4n+ez5m2u5d4PkZyw==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { - "node": ">=4.2.0" + "node": ">=14.17" } }, "node_modules/undici-types": { @@ -2506,22 +2770,6 @@ "integrity": "sha1-RQ1Nyfpw3nMnYvvS1KKJgUGaDM8=", "dev": true }, - "node_modules/vscode-test": { - "version": "1.6.1", - "resolved": "https://registry.npmjs.org/vscode-test/-/vscode-test-1.6.1.tgz", - "integrity": "sha512-086q88T2ca1k95mUzffvbzb7esqQNvJgiwY4h29ukPhFo8u+vXOOmelUoU5EQUHs3Of8+JuQ3oGdbVCqaxuTXA==", - "deprecated": "This package has been renamed to @vscode/test-electron, please update to the new name", - "dev": true, - "dependencies": { - "http-proxy-agent": "^4.0.1", - "https-proxy-agent": "^5.0.0", - "rimraf": "^3.0.2", - "unzipper": "^0.10.11" - }, - "engines": { - "node": ">=8.9.3" - } - }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", @@ -2537,58 +2785,6 @@ "node": ">= 8" } }, - "node_modules/wide-align": { - "version": "1.1.3", - "resolved": "https://registry.npmjs.org/wide-align/-/wide-align-1.1.3.tgz", - "integrity": "sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==", - "dev": true, - "dependencies": { - "string-width": "^1.0.2 || 2" - } - }, - "node_modules/wide-align/node_modules/ansi-regex": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-3.0.0.tgz", - "integrity": "sha1-7QMXwyIGT3lGbAKWa922Bas32Zg=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/is-fullwidth-code-point": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-2.0.0.tgz", - "integrity": "sha1-o7MKXE8ZkYMWeqq5O+764937ZU8=", - "dev": true, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/string-width": { - "version": "2.1.1", - "resolved": "https://registry.npmjs.org/string-width/-/string-width-2.1.1.tgz", - "integrity": "sha512-nOqH59deCq9SRHlxq1Aw85Jnt4w6KvLKqWVik6oA9ZklXLNIOlqg4F2yrT1MVaTjAqvVwdfeZ7w7aCvJD7ugkw==", - "dev": true, - "dependencies": { - "is-fullwidth-code-point": "^2.0.0", - "strip-ansi": "^4.0.0" - }, - "engines": { - "node": ">=4" - } - }, - "node_modules/wide-align/node_modules/strip-ansi": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-4.0.0.tgz", - "integrity": "sha1-qEeQIusaw2iocTibY1JixQXuNo8=", - "dev": true, - "dependencies": { - "ansi-regex": "^3.0.0" - }, - "engines": { - "node": ">=4" - } - }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", @@ -2600,9 +2796,9 @@ } }, "node_modules/workerpool": { - "version": "6.1.0", - "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.1.0.tgz", - "integrity": "sha512-toV7q9rWNYha963Pl/qyeZ6wG+3nnsyvolaNUS8+R5Wtw6qJPTxIlOP1ZSvcGhEJw+l3HMMmtiNo9Gl61G4GVg==", + "version": "6.5.1", + "resolved": "https://registry.npmjs.org/workerpool/-/workerpool-6.5.1.tgz", + "integrity": "sha512-Fs4dNYcsdpYSAfVxhnl1L5zTksjvOJxtC5hzMNl+1t9B8hTJTdKDyZ5ju7ztgPy+ft9tBFXoOlDNiOT9WUXZlA==", "dev": true }, "node_modules/wrap-ansi": { @@ -2622,39 +2818,24 @@ "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "node_modules/wrap-ansi-cjs": { + "name": "wrap-ansi", + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", + "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { - "color-convert": "^2.0.1" + "ansi-styles": "^4.0.0", + "string-width": "^4.1.0", + "strip-ansi": "^6.0.0" }, "engines": { - "node": ">=8" + "node": ">=10" }, "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/wrap-ansi/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" + "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, - "node_modules/wrap-ansi/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, "node_modules/wrappy": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", @@ -2689,9 +2870,9 @@ } }, "node_modules/yargs-parser": { - "version": "20.2.4", - "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.4.tgz", - "integrity": "sha512-WOkpgNhPTlE73h4VFAFsOnomJVaovO8VqLDzy5saChRBFQFBoMYirowyW+Q9HB4HFF4Z7VZTiG3iSzJJA29yRA==", + "version": "20.2.9", + "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-20.2.9.tgz", + "integrity": "sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==", "dev": true, "engines": { "node": ">=10" diff --git a/package.json b/package.json index 6748f00a..a05ae3e8 100644 --- a/package.json +++ b/package.json @@ -87,17 +87,16 @@ "ts-md5": "^1.3.1" }, "devDependencies": { - "@types/glob": "^7.1.4", "@types/mocha": "^8.2.3", "@types/node": "^18.11.13", "@types/tmp": "^0.2.6", "@types/vscode": "^1.85.0", "@typescript-eslint/eslint-plugin": "^5.30.0", "@typescript-eslint/parser": "^5.30.0", + "@vscode/test-electron": "^1.6.1", "eslint": "^8.18.0", - "glob": "^7.2.0", - "mocha": "^8.4.0", - "typescript": "^4.7.4", - "vscode-test": "^1.6.1" + "glob": "^11.0.0", + "mocha": "^10.7.3", + "typescript": "^5.4.2" } } diff --git a/src/test/runTest.ts b/src/test/runTest.ts index 1eabfa33..27b3ceb2 100644 --- a/src/test/runTest.ts +++ b/src/test/runTest.ts @@ -1,6 +1,6 @@ import * as path from 'path'; -import { runTests } from 'vscode-test'; +import { runTests } from '@vscode/test-electron'; async function main() { try { diff --git a/src/test/suite/index.ts b/src/test/suite/index.ts index 7029e38e..73408d7d 100644 --- a/src/test/suite/index.ts +++ b/src/test/suite/index.ts @@ -2,7 +2,7 @@ import * as path from 'path'; import * as Mocha from 'mocha'; import * as glob from 'glob'; -export function run(): Promise { +export async function run() { // Create the mocha test const mocha = new Mocha({ ui: 'tdd', @@ -11,28 +11,17 @@ export function run(): Promise { const testsRoot = path.resolve(__dirname, '..'); - return new Promise((c, e) => { - glob('**/**.test.js', { cwd: testsRoot }, (err, files) => { - if (err) { - return e(err); - } - - // Add files to the test suite - files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); - - try { - // Run the mocha test - mocha.run(failures => { - if (failures > 0) { - e(new Error(`${failures} tests failed.`)); - } else { - c(); - } - }); - } catch (err) { - console.error(err); - e(err); + const files = await glob.glob('**/**.test.js', { cwd: testsRoot }); + files.forEach(f => mocha.addFile(path.resolve(testsRoot, f))); + try { + // Run the mocha test + mocha.run(failures => { + if (failures > 0) { + throw new Error(`${failures} tests failed.`); } }); - }); + } catch (err) { + console.error(err); + throw err; + } } From 0dcdf328aafdbcc49c4167d86f8b63e2b7195bb6 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Sun, 29 Sep 2024 19:41:07 +0200 Subject: [PATCH 04/43] Run trace generator with the correct python binary --- src/extension.ts | 4 ++-- .../backend/backend.ts | 18 ++++++++++++++++++ .../backend/trace_generator.ts | 14 ++++++++++---- 3 files changed, 30 insertions(+), 6 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 00098c83..8ddca1fc 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -146,7 +146,7 @@ type PythonCmdResult = { kind: "warning", msg: string, cmd: string[] }; -function getPythonCmd(ext: PythonExtension): PythonCmdResult { +export function getPythonCmd(ext: PythonExtension): PythonCmdResult { const config = vscode.workspace.getConfiguration(extensionId); const hasConfig = config && config[python3ConfigKey]; if (hasConfig) { @@ -314,7 +314,7 @@ class TerminalLinkProvider implements vscode.TerminalLinkProvider { } } -class PythonExtension { +export class PythonExtension { private pyApi: any; constructor() { diff --git a/src/programflow-visualization/backend/backend.ts b/src/programflow-visualization/backend/backend.ts index 2cdbc995..ca2cc70e 100644 --- a/src/programflow-visualization/backend/backend.ts +++ b/src/programflow-visualization/backend/backend.ts @@ -1,8 +1,25 @@ import { ExtensionContext, Uri } from 'vscode'; import { MessagePort, Worker } from 'worker_threads'; +import { PythonExtension, getPythonCmd } from '../../extension'; import path = require('path'); export function startBackend(context: ExtensionContext, file: Uri): MessagePort { + const pyExt = new PythonExtension(); + const pythonCmdResult = getPythonCmd(pyExt); + let pythonCmd = ["python3"]; + switch (pythonCmdResult.kind) { + case "error": + console.error("Getting Python command failed:", pythonCmdResult.msg); + break; + case "warning": + console.error("Warning while getting Python command:", pythonCmdResult.msg); + pythonCmd = pythonCmdResult.cmd; + break; + case "success": + pythonCmd = pythonCmdResult.cmd; + break; + } + const mainPath = context.asAbsolutePath("python/deps/pytrace-generator/main.py"); const workerPath = path.resolve(__dirname, 'trace_generator.js'); const worker = new Worker(workerPath); @@ -10,6 +27,7 @@ export function startBackend(context: ExtensionContext, file: Uri): MessagePort worker.postMessage({ file: file.fsPath, mainPath: mainPath, + pythonCmd: pythonCmd, tracePort: channel.port1 }, [channel.port1]); return channel.port2; diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts index d633156f..d2c5c2ea 100644 --- a/src/programflow-visualization/backend/trace_generator.ts +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -1,10 +1,16 @@ import { spawn } from 'child_process'; import { MessagePort, isMainThread, parentPort } from 'worker_threads'; -export function generateTrace(mainPath: string, filePath: string, tracePort: MessagePort) { - const traceArgs = [mainPath, filePath]; +export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort) { + if (pythonCmd.length === 0) { + console.error("Python command is missing"); + tracePort.close(); + return; + } - const child = spawn("python3", traceArgs, { windowsHide: true }); + const traceArgs = [mainPath, filePath]; + const args = pythonCmd.slice(1).concat(traceArgs); + const child = spawn(pythonCmd[0], args, { windowsHide: true }); let err = ""; tracePort.on('close', () => { @@ -50,6 +56,6 @@ export function generateTrace(mainPath: string, filePath: string, tracePort: Mes if (!isMainThread && parentPort) { parentPort.once('message', (initParams) => { - generateTrace(initParams.mainPath, initParams.file, initParams.tracePort); + generateTrace(initParams.pythonCmd, initParams.mainPath, initParams.file, initParams.tracePort); }); } From b160635075242a0c51d6b47f1fc7472da67e8c8f Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 12:09:30 +0200 Subject: [PATCH 05/43] Visualization: use dirname of main file as working directory --- src/programflow-visualization/backend/trace_generator.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts index d2c5c2ea..fdc641a3 100644 --- a/src/programflow-visualization/backend/trace_generator.ts +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -1,4 +1,5 @@ import { spawn } from 'child_process'; +import { dirname } from 'path'; import { MessagePort, isMainThread, parentPort } from 'worker_threads'; export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort) { @@ -10,7 +11,7 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s const traceArgs = [mainPath, filePath]; const args = pythonCmd.slice(1).concat(traceArgs); - const child = spawn(pythonCmd[0], args, { windowsHide: true }); + const child = spawn(pythonCmd[0], args, { cwd: dirname(filePath), windowsHide: true }); let err = ""; tracePort.on('close', () => { From bf7b7546b0a93bdec667ed325fec9e69f9344580 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 12:30:08 +0200 Subject: [PATCH 06/43] Fix line highlighting for last step --- src/programflow-visualization/frontend/visualization_panel.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index 31cbcd13..bc8607ae 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -184,7 +184,7 @@ export class VisualizationPanel { editor = await vscode.window.showTextDocument(document); } - if (remove) { + if (remove || editor.document.lineCount < this._trace[this._traceIndex][0]) { editor.setDecorations(nextLineExecuteHighlightType, []); } else { this.setNextLineHighlighting(editor); @@ -195,7 +195,7 @@ export class VisualizationPanel { if (this._trace.length === 0) { return; } - const nextLine = this._traceIndex !== this._trace.length - 1 ? this._trace[this._traceIndex][0] - 1 : -1; + const nextLine = this._trace[this._traceIndex][0] - 1; if (nextLine > -1) { this.setEditorDecorations(editor, nextLineExecuteHighlightType, nextLine); From 22cbd06c567855a5da5b9b81dae9d4b09282beeb Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 12:39:13 +0200 Subject: [PATCH 07/43] Remove Node.js 12 CI The new version of TypeScript does no longer seem to be compatible with Node.js 12. --- .github/workflows/github-action-test-js.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-action-test-js.yml b/.github/workflows/github-action-test-js.yml index 46afa3d6..ddfa1223 100644 --- a/.github/workflows/github-action-test-js.yml +++ b/.github/workflows/github-action-test-js.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [12.x, 14.x, 16.x] + node-version: [14.x, 16.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: From 186e95f6a0e3c0a798fd184b0263c78bfa35a577 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 14:31:04 +0200 Subject: [PATCH 08/43] Use compile to supply a filename This allows us to not rely on the assumption that "" is always representing the filename of the main file. This led to problems in the skipping logic where some code executed through an import statement was assumed to be our code. That led to stack frames being popped (which shouldn't be popped at that point). --- python/deps/pytrace-generator/main.py | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 373a7d76..095ab2eb 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -266,8 +266,6 @@ def __init__(self): def trace_dispatch(self, frame, event, arg): filename = frame.f_code.co_filename - if filename == "": - filename = self.filename # Skip built-in modules # This might not be the best solution. Adjust if required. @@ -277,8 +275,6 @@ def trace_dispatch(self, frame, event, arg): elif not filename.startswith(os.path.dirname(self.filename)): skip = True self.skip_until = frame.f_back.f_code.co_filename - if self.skip_until == "": - self.skip_until = self.filename if skip: return self.trace_dispatch else: @@ -317,7 +313,8 @@ def trace_dispatch(self, frame, event, arg): def run_script(self, filename, script_str): self.filename = os.path.abspath(filename) - self.run(script_str) + code = compile(script_str, self.filename, "exec") + self.run(code) if len(sys.argv) <= 1: From be6c9963034c7775f8df932c921b3ae0b7cc9e0e Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 16:56:59 +0200 Subject: [PATCH 09/43] Don't try to HTML-escape null or undefined --- src/programflow-visualization/frontend/HTMLGenerator.ts | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 07cf024a..47078f18 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -1,5 +1,9 @@ function escapeHTML(s: any) { - return s.toString().replace(/[^0-9A-Za-z ]/g, (c: string) => "&#" + c.charCodeAt(0) + ";"); + if (s !== undefined && s !== null) { + return s.toString().replace(/[^0-9A-Za-z ]/g, (c: string) => "&#" + c.charCodeAt(0) + ";"); + } else { + return s; + } } export class HTMLGenerator { From 6a99b09cd9f4894ee1fc0ac698ff8f61aacc5e13 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 17:05:44 +0200 Subject: [PATCH 10/43] Remove str handling for Heap.store Strings are displayed on the stack again. --- python/deps/pytrace-generator/main.py | 3 --- 1 file changed, 3 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 095ab2eb..75dea814 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -189,9 +189,6 @@ def store(self, address, value): stored_value[key_id] = prim_value if prim_value.is_ref(): inner_values.append(v) - elif value_type == str: - # Leave it as it is - pass else: # Assume that there are no primitive types (int, bool, ...) passed to the top level heap # Only "real" objects are left From 2dd2cc6adbce1690ca601f005e998183f9dcb4b1 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 17:37:17 +0200 Subject: [PATCH 11/43] Don't call getattr twice --- python/deps/pytrace-generator/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 75dea814..231c2d0d 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -202,7 +202,7 @@ def store(self, address, value): if field.startswith("__") or callable(v): continue - prim_value = PrimitiveValue(getattr(value, field)) + prim_value = PrimitiveValue(v) stored_value[field] = prim_value if prim_value.is_ref(): inner_values.append(v) From f383c64023909fb3930f15d56586735324aeee90 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 17:45:39 +0200 Subject: [PATCH 12/43] Show an empty object for generators This can otherwise cause all sorts of issues. Also, the internals of the generator object are not that important for beginners. --- python/deps/pytrace-generator/main.py | 3 +++ 1 file changed, 3 insertions(+) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 231c2d0d..039697aa 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -1,6 +1,7 @@ import bdb import copy from dataclasses import dataclass +import inspect import json import math import os @@ -189,6 +190,8 @@ def store(self, address, value): stored_value[key_id] = prim_value if prim_value.is_ref(): inner_values.append(v) + elif inspect.isgenerator(value): + stored_value = {} else: # Assume that there are no primitive types (int, bool, ...) passed to the top level heap # Only "real" objects are left From dea0b1844f999d93e5d646ca2bf859cb6f099e71 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 18:19:49 +0200 Subject: [PATCH 13/43] Ignore modules and classes for visualization --- python/deps/pytrace-generator/main.py | 34 ++++++++++++++----- .../test/test-cases/importArgparse.py.json | 11 ++++++ .../test/test-cases/importShutil.py.json | 11 ++++++ .../test/test-cases/importSubprocess.py.json | 11 ++++++ .../test/test-cases/importTime.py | 2 +- .../test/test-cases/importTime.py.json | 13 ++++++- 6 files changed, 71 insertions(+), 11 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 039697aa..d606bd0e 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -202,7 +202,7 @@ def store(self, address, value): except: # Just ignore it in case getattr fails continue - if field.startswith("__") or callable(v): + if should_ignore_on_heap(field, v): continue prim_value = PrimitiveValue(v) @@ -234,14 +234,33 @@ def format(self): } +def should_ignore(variable_name, value, ignore_list = []): + if variable_name in ignore_list or variable_name.startswith("__"): + return True + if inspect.isfunction(value) or inspect.ismethod(value) or inspect.isbuiltin(value): + return True + if inspect.ismodule(value): + return True + if inspect.isclass(value): + return True + return False + +def should_ignore_on_stack(variable_name, value, ignore_list = []): + if should_ignore(variable_name, value, ignore_list): + return True + return False + +def should_ignore_on_heap(variable_name, value, ignore_list = []): + if should_ignore(variable_name, value, ignore_list): + return True + return False + + def generate_heap(frame, ignore): heap = Heap() while True: for variable_name in frame.f_locals: - if variable_name in ignore or variable_name.startswith("__"): - continue - if callable(frame.f_locals[variable_name]) and frame.f_code.co_qualname == "": - # Skip top-level functions + if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], ignore): continue if primitive_type(type(frame.f_locals[variable_name])) != "ref": continue @@ -292,10 +311,7 @@ def trace_dispatch(self, frame, event, arg): self.stack.pop_frame() elif event == "line": for variable_name in frame.f_locals: - if variable_name in self.stack_ignore or variable_name.startswith("__"): - continue - if type(frame.f_locals[variable_name]) == types.FunctionType: - # Skip functions + if should_ignore_on_stack(variable_name, frame.f_locals[variable_name],self.stack_ignore): continue self.stack.push(variable_name, frame.f_locals[variable_name]) heap = generate_heap(frame, self.stack_ignore) diff --git a/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json b/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json index 51992f5e..a8d44894 100644 --- a/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json +++ b/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json @@ -1,4 +1,15 @@ [ + { + "line": 1, + "filePath": "importArgparse.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, { "line": 3, "filePath": "importArgparse.py", diff --git a/python/deps/pytrace-generator/test/test-cases/importShutil.py.json b/python/deps/pytrace-generator/test/test-cases/importShutil.py.json index 9f952a4b..e8b5da02 100644 --- a/python/deps/pytrace-generator/test/test-cases/importShutil.py.json +++ b/python/deps/pytrace-generator/test/test-cases/importShutil.py.json @@ -1,4 +1,15 @@ [ + { + "line": 1, + "filePath": "importShutil.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, { "line": 3, "filePath": "importShutil.py", diff --git a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json index 420bf012..25cc564c 100644 --- a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json +++ b/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json @@ -1,4 +1,15 @@ [ + { + "line": 1, + "filePath": "importSubprocess.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, { "line": 3, "filePath": "importSubprocess.py", diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py b/python/deps/pytrace-generator/test/test-cases/importTime.py index bd35e428..6693bcc0 100644 --- a/python/deps/pytrace-generator/test/test-cases/importTime.py +++ b/python/deps/pytrace-generator/test/test-cases/importTime.py @@ -1 +1 @@ -import time +import time \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py.json b/python/deps/pytrace-generator/test/test-cases/importTime.py.json index ff0b975b..873d3fe0 100644 --- a/python/deps/pytrace-generator/test/test-cases/importTime.py.json +++ b/python/deps/pytrace-generator/test/test-cases/importTime.py.json @@ -1,6 +1,17 @@ [ { - "line": 3, + "line": 1, + "filePath": "importTime.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 2, "filePath": "importTime.py", "stack": [ { From 1a511e9fea04b8e2be0a7d33f71e46020a4221c1 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 18:45:04 +0200 Subject: [PATCH 14/43] Ignore frames for visualization --- python/deps/pytrace-generator/main.py | 2 ++ 1 file changed, 2 insertions(+) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index d606bd0e..00f4cf2a 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -243,6 +243,8 @@ def should_ignore(variable_name, value, ignore_list = []): return True if inspect.isclass(value): return True + if inspect.isframe(value): + return True return False def should_ignore_on_stack(variable_name, value, ignore_list = []): From 64d3a22788137e35a69059e67d76364368b1f238 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 19:18:22 +0200 Subject: [PATCH 15/43] Trace generator: add the script's directory to path --- python/deps/pytrace-generator/main.py | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 00f4cf2a..9a784fd8 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -330,7 +330,7 @@ def trace_dispatch(self, frame, event, arg): return self.trace_dispatch def run_script(self, filename, script_str): - self.filename = os.path.abspath(filename) + self.filename = filename code = compile(script_str, self.filename, "exec") self.run(code) @@ -340,7 +340,7 @@ def run_script(self, filename, script_str): eprint("usage: python main.py file.py") exit(1) -filename = sys.argv[1] +filename = os.path.abspath(sys.argv[1]) with open(filename, "r") as f: script_str = f.read() @@ -348,5 +348,8 @@ def run_script(self, filename, script_str): # It's a bit hacky but probably the easiest solution script_str += "\npass\n" +# Add script directory to path +sys.path.insert(0, os.path.dirname(filename)) + debugger = PyTraceGenerator() debugger.run_script(filename, script_str) From 58378be299eb08d93bd85359a6f3b840ee402232 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 30 Sep 2024 19:47:03 +0200 Subject: [PATCH 16/43] Trace generator: ignore objects imported from other modules --- python/deps/pytrace-generator/main.py | 28 +++++++++++-------- .../test/test-cases/importWypp.py | 1 + .../test/test-cases/importWypp.py.json | 24 ++++++++++++++++ .../test/test-cases/module1.py | 3 ++ .../test/test-cases/module2.py | 5 ++++ 5 files changed, 49 insertions(+), 12 deletions(-) create mode 100644 python/deps/pytrace-generator/test/test-cases/importWypp.py create mode 100644 python/deps/pytrace-generator/test/test-cases/importWypp.py.json create mode 100644 python/deps/pytrace-generator/test/test-cases/module1.py create mode 100644 python/deps/pytrace-generator/test/test-cases/module2.py diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 9a784fd8..b9f34c1d 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -155,7 +155,8 @@ def format(self): return [frame.format() for frame in self.frames] class Heap: - def __init__(self): + def __init__(self, script_path): + self.script_path = script_path self.memory = {} def store(self, address, value): @@ -202,7 +203,7 @@ def store(self, address, value): except: # Just ignore it in case getattr fails continue - if should_ignore_on_heap(field, v): + if should_ignore_on_heap(field, v, self.script_path): continue prim_value = PrimitiveValue(v) @@ -234,7 +235,7 @@ def format(self): } -def should_ignore(variable_name, value, ignore_list = []): +def should_ignore(variable_name, value, script_path, ignore_list = []): if variable_name in ignore_list or variable_name.startswith("__"): return True if inspect.isfunction(value) or inspect.ismethod(value) or inspect.isbuiltin(value): @@ -245,24 +246,27 @@ def should_ignore(variable_name, value, ignore_list = []): return True if inspect.isframe(value): return True + if hasattr(value, "__module__"): + if not sys.modules[value.__module__].__file__.startswith(script_path): + return True return False -def should_ignore_on_stack(variable_name, value, ignore_list = []): - if should_ignore(variable_name, value, ignore_list): +def should_ignore_on_stack(variable_name, value, script_path, ignore_list = []): + if should_ignore(variable_name, value, script_path, ignore_list): return True return False -def should_ignore_on_heap(variable_name, value, ignore_list = []): - if should_ignore(variable_name, value, ignore_list): +def should_ignore_on_heap(variable_name, value, script_path, ignore_list = []): + if should_ignore(variable_name, value, script_path, ignore_list): return True return False -def generate_heap(frame, ignore): - heap = Heap() +def generate_heap(frame, script_path, ignore): + heap = Heap(script_path) while True: for variable_name in frame.f_locals: - if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], ignore): + if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], script_path, ignore): continue if primitive_type(type(frame.f_locals[variable_name])) != "ref": continue @@ -313,10 +317,10 @@ def trace_dispatch(self, frame, event, arg): self.stack.pop_frame() elif event == "line": for variable_name in frame.f_locals: - if should_ignore_on_stack(variable_name, frame.f_locals[variable_name],self.stack_ignore): + if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore): continue self.stack.push(variable_name, frame.f_locals[variable_name]) - heap = generate_heap(frame, self.stack_ignore) + heap = generate_heap(frame, self.filename, self.stack_ignore) step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) diff --git a/python/deps/pytrace-generator/test/test-cases/importWypp.py b/python/deps/pytrace-generator/test/test-cases/importWypp.py new file mode 100644 index 00000000..b0957632 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importWypp.py @@ -0,0 +1 @@ +from wypp import * \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/importWypp.py.json b/python/deps/pytrace-generator/test/test-cases/importWypp.py.json new file mode 100644 index 00000000..69f974f8 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/importWypp.py.json @@ -0,0 +1,24 @@ +[ + { + "line": 1, + "filePath": "importWypp.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 2, + "filePath": "importWypp.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + } +] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/test-cases/module1.py b/python/deps/pytrace-generator/test/test-cases/module1.py new file mode 100644 index 00000000..6acd97b8 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/module1.py @@ -0,0 +1,3 @@ +# This test ensures that module2 can be imported and used +import module2 +b = module2.myfun(1) diff --git a/python/deps/pytrace-generator/test/test-cases/module2.py b/python/deps/pytrace-generator/test/test-cases/module2.py new file mode 100644 index 00000000..67670c9e --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/module2.py @@ -0,0 +1,5 @@ +def myfun(i): + i += 1 + i += 1 + i += 1 + return i From 604a784e3ad7cf13398a0962f851b4f1246048b5 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Wed, 2 Oct 2024 08:26:06 +0200 Subject: [PATCH 17/43] more node versions for CI --- .github/workflows/github-action-test-js.yml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/.github/workflows/github-action-test-js.yml b/.github/workflows/github-action-test-js.yml index ddfa1223..8002bf67 100644 --- a/.github/workflows/github-action-test-js.yml +++ b/.github/workflows/github-action-test-js.yml @@ -13,7 +13,7 @@ jobs: strategy: matrix: - node-version: [14.x, 16.x] + node-version: [14.x, 16.x, 18.x, 20.x] # See supported Node.js release schedule at https://nodejs.org/en/about/releases/ steps: From 3c5993f13803d952ac5f9b236fb5c25aa98218d2 Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Wed, 2 Oct 2024 08:26:17 +0200 Subject: [PATCH 18/43] update README --- README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index 05fe9183..92a51de3 100644 --- a/README.md +++ b/README.md @@ -12,7 +12,7 @@ The ideas of this environment are based on the great ideas from * Step 1. Install **Python 3.10.x** or **Python 3.11.x** or **Python 3.12.x**. * Step 2. Install the **write-your-python-program** extension. You need at least Visual Studio Code - version 1.49.0. + version 1.85.0. * Step 3. Open or create a Python file. The "RUN" button in the taskbar at the bottom will run your file with the teaching language provided by write-your-python-program. @@ -241,6 +241,7 @@ Please report them in the [issue tracker](https://github.com/skogsbaer/write-you You can debug the extension from Visual Studio Code: +* `npm install` * Open the main folder of the plugin with vscode. * Open the file `extension.ts`. * Choose "Run" from the menu, then "Start Debugging". From f214416adbb437c0eb5036a2dd74ea21b8c4700a Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Tue, 8 Oct 2024 18:54:07 +0200 Subject: [PATCH 19/43] Send generated trace via TCP to the plugin This avoids mixing up the program's stdout with the trace. --- python/deps/pytrace-generator/main.py | 28 ++++-- .../test/generateExpectation.py | 18 +++- .../deps/pytrace-generator/test/runTests.py | 18 +++- python/deps/pytrace-generator/test/util.py | 12 +-- .../backend/trace_generator.ts | 93 +++++++++++-------- 5 files changed, 113 insertions(+), 56 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index b9f34c1d..eb4de24b 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -6,6 +6,7 @@ import math import os import re +import socket import sys import types @@ -281,8 +282,9 @@ def generate_heap(frame, script_path, ignore): class PyTraceGenerator(bdb.Bdb): - def __init__(self): + def __init__(self, trace_socket): super().__init__() + self.trace_socket = trace_socket self.stack = Stack() self.stack_ignore = [] self.init = False @@ -325,10 +327,11 @@ def trace_dispatch(self, frame, event, arg): step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) # Output trace - json_str = json.dumps(step.format()).encode('utf-8') - json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) - sys.stdout.buffer.write(json_len) - sys.stdout.buffer.write(json_str) + if self.trace_socket is not None: + json_str = json.dumps(step.format()).encode('utf-8') + json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) + self.trace_socket.sendall(json_len) + self.trace_socket.sendall(json_str) # TODO exception return self.trace_dispatch @@ -341,7 +344,7 @@ def run_script(self, filename, script_str): if len(sys.argv) <= 1: eprint("not enough arguments") - eprint("usage: python main.py file.py") + eprint("usage: python main.py file.py [port]") exit(1) filename = os.path.abspath(sys.argv[1]) @@ -355,5 +358,16 @@ def run_script(self, filename, script_str): # Add script directory to path sys.path.insert(0, os.path.dirname(filename)) -debugger = PyTraceGenerator() +trace_socket = None +try: + if len(sys.argv) > 2: + trace_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + trace_socket.connect(("127.0.0.1", int(sys.argv[2]))) +except: + trace_socket = None + +debugger = PyTraceGenerator(trace_socket) debugger.run_script(filename, script_str) + +if trace_socket is not None: + trace_socket.close() diff --git a/python/deps/pytrace-generator/test/generateExpectation.py b/python/deps/pytrace-generator/test/generateExpectation.py index 85d3433d..d99bd057 100644 --- a/python/deps/pytrace-generator/test/generateExpectation.py +++ b/python/deps/pytrace-generator/test/generateExpectation.py @@ -1,5 +1,6 @@ import json import os +import socket import subprocess import sys @@ -8,12 +9,25 @@ if __name__ == '__main__': python_file = sys.argv[1] trace_file = f"{sys.argv[1]}.json" + trace_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + trace_socket.bind(("127.0.0.1", 0)) + trace_socket.listen(1) + port = trace_socket.getsockname()[1] p = subprocess.run( - ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file], + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)], capture_output=True ) + connection, address = trace_socket.accept() + data = bytearray() + while True: + buf = connection.recv(4096) + if len(buf) == 0: + break + data.extend(buf) + connection.close() + trace_socket.close() if p.returncode != 0: raise RuntimeError("trace generator did not exit with exit code 0") - trace_entries = read_trace_entries(p.stdout) + trace_entries = read_trace_entries(data) with open(trace_file, "w") as f: json.dump(trace_entries, f, indent="\t") diff --git a/python/deps/pytrace-generator/test/runTests.py b/python/deps/pytrace-generator/test/runTests.py index dde75d6e..4bcd569b 100644 --- a/python/deps/pytrace-generator/test/runTests.py +++ b/python/deps/pytrace-generator/test/runTests.py @@ -1,5 +1,6 @@ import json import os +import socket import subprocess import unittest @@ -10,13 +11,26 @@ class TestTraceGeneration(unittest.TestCase): def generate_test(python_file, expected_trace_path): def test(self): + trace_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) + trace_socket.bind(("127.0.0.1", 0)) + trace_socket.listen(1) + port = trace_socket.getsockname()[1] p = subprocess.run( - ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file], + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)], capture_output=True ) + connection, address = trace_socket.accept() + data = bytearray() + while True: + buf = connection.recv(4096) + if len(buf) == 0: + break + data.extend(buf) + connection.close() + trace_socket.close() self.assertEqual(p.returncode, 0) if expected_trace_path is not None: - trace_entries = read_trace_entries(p.stdout) + trace_entries = read_trace_entries(data) with open(expected_trace_path) as f: expected_trace = json.load(f) self.assertEqual(trace_entries, expected_trace) diff --git a/python/deps/pytrace-generator/test/util.py b/python/deps/pytrace-generator/test/util.py index 779dc34e..22a43f51 100644 --- a/python/deps/pytrace-generator/test/util.py +++ b/python/deps/pytrace-generator/test/util.py @@ -1,16 +1,16 @@ import json -def read_trace_entries(stdout): +def read_trace_entries(data): entries = [] - while len(stdout) > 0: - entry_len = int.from_bytes(stdout[:4], byteorder="big", signed=False) - stdout = stdout[4:] - entry = json.loads(str(stdout[:entry_len], encoding="utf-8")) + while len(data) > 0: + entry_len = int.from_bytes(data[:4], byteorder="big", signed=False) + data = data[4:] + entry = json.loads(str(data[:entry_len], encoding="utf-8")) test_cases_str = "test-cases/" idx = entry["filePath"].rfind(test_cases_str) if idx >= 0: # Remove user specific path stuff to allow for equality testing entry["filePath"] = entry["filePath"][idx + len(test_cases_str):] entries.append(entry) - stdout = stdout[entry_len:] + data = data[entry_len:] return entries diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts index fdc641a3..44338fc9 100644 --- a/src/programflow-visualization/backend/trace_generator.ts +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -1,4 +1,5 @@ -import { spawn } from 'child_process'; +import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { AddressInfo, createServer } from 'net'; import { dirname } from 'path'; import { MessagePort, isMainThread, parentPort } from 'worker_threads'; @@ -9,49 +10,63 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s return; } - const traceArgs = [mainPath, filePath]; - const args = pythonCmd.slice(1).concat(traceArgs); - const child = spawn(pythonCmd[0], args, { cwd: dirname(filePath), windowsHide: true }); - let err = ""; + let childRef: ChildProcessWithoutNullStreams[] = []; + let buffer = Buffer.alloc(0); + const server = createServer((socket) => { + socket.on('data', (data) => { + buffer = Buffer.concat([buffer, data]); + while (buffer.length >= 4) { + const dataLen = buffer.readUint32BE(0); + if (buffer.length >= 4 + dataLen) { + const traceStr = buffer.subarray(4, 4 + dataLen).toString(); + try { + const traceElem = JSON.parse(traceStr); + tracePort.postMessage(traceElem); + buffer = buffer.subarray(4 + dataLen); + } catch (error) { + console.error('JSON parsing of trace element failed:', error); + tracePort.close(); + if (childRef.length > 0) { + childRef[0].kill(); + } + } + } else { + break; + } + } + }); - tracePort.on('close', () => { - child.kill(); + socket.on('end', () => { + tracePort.close(); + }); }); + server.listen(0, '127.0.0.1', () => { + const port = (server.address() as AddressInfo)?.port; + const traceArgs = [mainPath, filePath, port.toString()]; + const args = pythonCmd.slice(1).concat(traceArgs); + const child = spawn(pythonCmd[0], args, { cwd: dirname(filePath), windowsHide: true }); + childRef.push(child); + let err = ""; - let buffer = Buffer.alloc(0); + tracePort.on('close', () => { + child.kill(); + }); - child.stdout.on('data', (data) => { - buffer = Buffer.concat([buffer, data]); - while (buffer.length >= 4) { - const dataLen = buffer.readUint32BE(0); - if (buffer.length >= 4 + dataLen) { - const traceStr = buffer.subarray(4, 4 + dataLen).toString(); - try { - const traceElem = JSON.parse(traceStr); - tracePort.postMessage(traceElem); - buffer = buffer.subarray(4 + dataLen); - } catch (error) { - console.error("JSON parsing of trace element failed:", error); - tracePort.close(); - child.kill(); - } - } else { - break; + child.stdout.on('data', (data) => { + console.log(data.toString()); + }); + child.stderr.on('data', (data) => { + err += data.toString(); + }); + child.on('close', (code) => { + if (code !== 0) { + console.error(`trace generator failed with code ${code}: ${err}`); } - } - }); - child.stderr.on('data', (data) => { - err += data.toString(); - }); - child.on('close', (code) => { - if (code !== 0) { - console.error(`trace generator failed with code ${code}: ${err}`); - } - tracePort.close(); - }); - child.on('error', (error) => { - console.error(error); - tracePort.close(); + }); + child.on('error', (error) => { + console.error(error); + tracePort.close(); + }); }); } From d66848ed943c1c20ac09a3561266f74a2e546f75 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 9 Oct 2024 19:43:04 +0200 Subject: [PATCH 20/43] Show functions and methods in visualization --- python/deps/pytrace-generator/main.py | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index eb4de24b..ba294d7f 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -239,7 +239,7 @@ def format(self): def should_ignore(variable_name, value, script_path, ignore_list = []): if variable_name in ignore_list or variable_name.startswith("__"): return True - if inspect.isfunction(value) or inspect.ismethod(value) or inspect.isbuiltin(value): + if inspect.isbuiltin(value): return True if inspect.ismodule(value): return True From 6feab6b6131bb2d539ef61390fb1579f9142f025 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 9 Oct 2024 21:27:56 +0200 Subject: [PATCH 21/43] Fix running trace generator tests for large outputs --- python/deps/pytrace-generator/test/generateExpectation.py | 8 ++++---- python/deps/pytrace-generator/test/runTests.py | 8 ++++---- 2 files changed, 8 insertions(+), 8 deletions(-) diff --git a/python/deps/pytrace-generator/test/generateExpectation.py b/python/deps/pytrace-generator/test/generateExpectation.py index d99bd057..805b60ec 100644 --- a/python/deps/pytrace-generator/test/generateExpectation.py +++ b/python/deps/pytrace-generator/test/generateExpectation.py @@ -13,9 +13,8 @@ trace_socket.bind(("127.0.0.1", 0)) trace_socket.listen(1) port = trace_socket.getsockname()[1] - p = subprocess.run( - ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)], - capture_output=True + p = subprocess.Popen( + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)] ) connection, address = trace_socket.accept() data = bytearray() @@ -26,7 +25,8 @@ data.extend(buf) connection.close() trace_socket.close() - if p.returncode != 0: + returncode = p.wait() + if returncode != 0: raise RuntimeError("trace generator did not exit with exit code 0") trace_entries = read_trace_entries(data) with open(trace_file, "w") as f: diff --git a/python/deps/pytrace-generator/test/runTests.py b/python/deps/pytrace-generator/test/runTests.py index 4bcd569b..6d1335d2 100644 --- a/python/deps/pytrace-generator/test/runTests.py +++ b/python/deps/pytrace-generator/test/runTests.py @@ -15,9 +15,8 @@ def test(self): trace_socket.bind(("127.0.0.1", 0)) trace_socket.listen(1) port = trace_socket.getsockname()[1] - p = subprocess.run( - ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)], - capture_output=True + p = subprocess.Popen( + ["python3", os.path.join(os.path.dirname(os.path.dirname(__file__)), "main.py"), python_file, str(port)] ) connection, address = trace_socket.accept() data = bytearray() @@ -28,7 +27,8 @@ def test(self): data.extend(buf) connection.close() trace_socket.close() - self.assertEqual(p.returncode, 0) + returncode = p.wait() + self.assertEqual(returncode, 0) if expected_trace_path is not None: trace_entries = read_trace_entries(data) with open(expected_trace_path) as f: From ff827b23abf60c41a5f6e6984a6a7abb1d43b02e Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 9 Oct 2024 22:43:11 +0200 Subject: [PATCH 22/43] Improve exclusion of imported variables --- python/deps/pytrace-generator/main.py | 24 +++++++++++++++++++++--- 1 file changed, 21 insertions(+), 3 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index ba294d7f..e1b6e0bd 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -3,6 +3,7 @@ from dataclasses import dataclass import inspect import json +import linecache import math import os import re @@ -14,6 +15,7 @@ def eprint(*args, **kwargs): print(*args, file=sys.stderr, **kwargs) type_name_regex = re.compile("") +import_regex = re.compile(r"^(?:[^'\"]+\s)?import ") # Frame objects: # https://docs.python.org/3/reference/datamodel.html#frame-objects @@ -155,6 +157,13 @@ def push(self, variable_name, value): def format(self): return [frame.format() for frame in self.frames] + def contains(self, variable_name): + for frame in self.frames: + for var in frame.values: + if var.variable_name == variable_name: + return True + return False + class Heap: def __init__(self, script_path): self.script_path = script_path @@ -247,9 +256,6 @@ def should_ignore(variable_name, value, script_path, ignore_list = []): return True if inspect.isframe(value): return True - if hasattr(value, "__module__"): - if not sys.modules[value.__module__].__file__.startswith(script_path): - return True return False def should_ignore_on_stack(variable_name, value, script_path, ignore_list = []): @@ -290,6 +296,7 @@ def __init__(self, trace_socket): self.init = False self.filename = "" self.skip_until = None + self.import_following = False def trace_dispatch(self, frame, event, arg): filename = frame.f_code.co_filename @@ -312,6 +319,17 @@ def trace_dispatch(self, frame, event, arg): self.init = True for variable_name in frame.f_locals: self.stack_ignore.append(variable_name) + elif self.import_following: + # Ignore new variables introduced through import + for variable_name in frame.f_locals: + if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore): + continue + if not self.stack.contains(variable_name): + self.stack_ignore.append(variable_name) + + # Check if the next line will be an import + next_source_line = linecache.getline(filename, line).strip() + self.import_following = import_regex.search(next_source_line) is not None if event == "call": self.stack.push_frame(frame) From c2030eeeb0c5e81ef73c750e3be3abf80f3447d1 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 9 Oct 2024 22:45:16 +0200 Subject: [PATCH 23/43] Try to normalize references to heap values in tests --- .../test/test-cases/factorial.py.json | 125 +++++++++++++++--- python/deps/pytrace-generator/test/util.py | 22 +++ 2 files changed, 132 insertions(+), 15 deletions(-) diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py.json b/python/deps/pytrace-generator/test/test-cases/factorial.py.json index c34d7ca6..66a9a324 100644 --- a/python/deps/pytrace-generator/test/test-cases/factorial.py.json +++ b/python/deps/pytrace-generator/test/test-cases/factorial.py.json @@ -16,10 +16,22 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 2, @@ -27,7 +39,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -40,7 +58,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 4, @@ -48,7 +72,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -61,7 +91,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 2, @@ -69,7 +105,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -92,7 +134,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 3, @@ -100,7 +148,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -123,7 +177,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 5, @@ -131,7 +191,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -149,7 +215,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 6, @@ -157,7 +229,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] }, { "frameName": "factorical", @@ -180,7 +258,13 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } }, { "line": 10, @@ -189,6 +273,11 @@ { "frameName": "", "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + }, { "type": "int", "value": 2, @@ -197,6 +286,12 @@ ] } ], - "heap": {} + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } } ] \ No newline at end of file diff --git a/python/deps/pytrace-generator/test/util.py b/python/deps/pytrace-generator/test/util.py index 22a43f51..5e288ccc 100644 --- a/python/deps/pytrace-generator/test/util.py +++ b/python/deps/pytrace-generator/test/util.py @@ -11,6 +11,28 @@ def read_trace_entries(data): if idx >= 0: # Remove user specific path stuff to allow for equality testing entry["filePath"] = entry["filePath"][idx + len(test_cases_str):] + normalize_refs(entry) entries.append(entry) data = data[entry_len:] return entries + +def normalize_refs(entry): + next_id = 0 + mapping = {} + for frame in entry["stack"]: + for var in frame["locals"]: + if var["type"] == "ref": + original_id = var["value"] + if original_id in mapping: + var["value"] = mapping[original_id] + else: + var["value"] = next_id + mapping[original_id] = next_id + if str(next_id) in entry["heap"]: + tmp = entry["heap"][str(next_id)] + entry["heap"][str(next_id)] = entry["heap"][str(original_id)] + entry["heap"][str(original_id)] = tmp + else: + entry["heap"][str(next_id)] = entry["heap"][str(original_id)] + del entry["heap"][str(original_id)] + next_id += 1 From 3d3acdfd8591f7d63dd710d7b56d363677752ec2 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 9 Oct 2024 22:56:10 +0200 Subject: [PATCH 24/43] Add a simple trace test case for WYPP --- .../test/test-cases/wyppSimple.py | 14 + .../test/test-cases/wyppSimple.py.json | 318 ++++++++++++++++++ 2 files changed, 332 insertions(+) create mode 100644 python/deps/pytrace-generator/test/test-cases/wyppSimple.py create mode 100644 python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py new file mode 100644 index 00000000..edeb2569 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py @@ -0,0 +1,14 @@ +from __future__ import annotations +from wypp import * + +@record +class Bar: + baz: str + +def generate_bar(): + return Bar("baz value") + +obj1 = Bar("baz value") +obj2 = generate_bar() + +check(obj1, obj2) diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json new file mode 100644 index 00000000..9288a002 --- /dev/null +++ b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -0,0 +1,318 @@ +[ + { + "line": 1, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 2, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 4, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 5, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 4, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "Bar", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 6, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "Bar", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 4, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 5, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 8, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + } + ], + "heap": {} + }, + { + "line": 11, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "generate_bar" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } + }, + { + "line": 12, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + } + }, + { + "line": 9, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + } + ] + }, + { + "frameName": "generate_bar", + "locals": [] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + } + }, + { + "line": 14, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + }, + { + "type": "ref", + "value": 2, + "name": "obj2" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + }, + "2": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + } + }, + { + "line": 16, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + }, + { + "type": "ref", + "value": 2, + "name": "obj2" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + }, + "2": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + } + } +] \ No newline at end of file From 4bbfe8cc916c15094e5f2cc6eaad4191fa0ecbfc Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 10:29:00 +0200 Subject: [PATCH 25/43] Install WYPP before generating trace --- .../backend/backend.ts | 4 +++ .../backend/trace_generator.ts | 33 ++++++++++++++----- 2 files changed, 29 insertions(+), 8 deletions(-) diff --git a/src/programflow-visualization/backend/backend.ts b/src/programflow-visualization/backend/backend.ts index ca2cc70e..19e4017c 100644 --- a/src/programflow-visualization/backend/backend.ts +++ b/src/programflow-visualization/backend/backend.ts @@ -20,6 +20,9 @@ export function startBackend(context: ExtensionContext, file: Uri): MessagePort break; } + // Get WYPP path + const runYourProgramPath = context.asAbsolutePath("python/src/runYourProgram.py"); + const mainPath = context.asAbsolutePath("python/deps/pytrace-generator/main.py"); const workerPath = path.resolve(__dirname, 'trace_generator.js'); const worker = new Worker(workerPath); @@ -28,6 +31,7 @@ export function startBackend(context: ExtensionContext, file: Uri): MessagePort file: file.fsPath, mainPath: mainPath, pythonCmd: pythonCmd, + runYourProgramPath: runYourProgramPath, tracePort: channel.port1 }, [channel.port1]); return channel.port2; diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts index 44338fc9..6c2a6a59 100644 --- a/src/programflow-visualization/backend/trace_generator.ts +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -1,15 +1,14 @@ -import { ChildProcessWithoutNullStreams, spawn } from 'child_process'; +import { ChildProcessWithoutNullStreams, execFile, ExecFileException, spawn } from 'child_process'; import { AddressInfo, createServer } from 'net'; import { dirname } from 'path'; import { MessagePort, isMainThread, parentPort } from 'worker_threads'; -export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort) { - if (pythonCmd.length === 0) { - console.error("Python command is missing"); - tracePort.close(); - return; - } +function installWypp(pythonCmd: string[], runYourProgramPath: string, callback: (error: ExecFileException | null, stdout: string, stderr: string) => void) { + const args = pythonCmd.slice(1).concat([runYourProgramPath, '--install-mode', 'installOnly']); + execFile(pythonCmd[0], args, { windowsHide: true }, callback); +} +export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort) { let childRef: ChildProcessWithoutNullStreams[] = []; let buffer = Buffer.alloc(0); const server = createServer((socket) => { @@ -72,6 +71,24 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s if (!isMainThread && parentPort) { parentPort.once('message', (initParams) => { - generateTrace(initParams.pythonCmd, initParams.mainPath, initParams.file, initParams.tracePort); + if (initParams.pythonCmd.length === 0) { + console.error("Python command is missing"); + initParams.tracePort.close(); + return; + } + installWypp(initParams.pythonCmd, initParams.runYourProgramPath, (error, stdout, stderr) => { + if (stdout.length > 0) { + console.log(stdout); + } + if (stderr.length > 0) { + console.error(stderr); + } + if (error !== null) { + console.error(error); + initParams.tracePort.close(); + return; + } + generateTrace(initParams.pythonCmd, initParams.mainPath, initParams.file, initParams.tracePort); + }); }); } From a31fa7cf614cc10a1d4ad37a4abc49bd44b881f7 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 10:52:52 +0200 Subject: [PATCH 26/43] Add file name to tab title of visualization --- src/programflow-visualization/frontend/frontend.ts | 3 ++- .../frontend/visualization_panel.ts | 7 ++++--- src/programflow-visualization/main.ts | 2 +- 3 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/programflow-visualization/frontend/frontend.ts b/src/programflow-visualization/frontend/frontend.ts index c1703ae3..3a73b686 100644 --- a/src/programflow-visualization/frontend/frontend.ts +++ b/src/programflow-visualization/frontend/frontend.ts @@ -5,6 +5,7 @@ import * as TraceCache from '../trace_cache'; export async function startFrontend( context: ExtensionContext, + filePath: string, fileHash: string, tracePort: MessagePort | null): Promise { var trace: BackendTrace = []; @@ -12,7 +13,7 @@ export async function startFrontend( trace = await TraceCache.getTrace(context, fileHash); } - const panel = await VisualizationPanel.getVisualizationPanel(context, fileHash, trace, tracePort); + const panel = await VisualizationPanel.getVisualizationPanel(context, filePath, fileHash, trace, tracePort); if (!panel) { return failure("Frontend couldn't be initialized!"); } diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index bc8607ae..87cee1cd 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -19,7 +19,7 @@ export class VisualizationPanel { private _traceIndex: number; private _tracePortSelfClose: boolean; - private constructor(context: vscode.ExtensionContext, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null) { + private constructor(context: vscode.ExtensionContext, filePath: string, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null) { this._fileHash = fileHash; this._tracePort = tracePort; this._backendTrace = { trace: trace, complete: trace.length > 0 }; @@ -31,7 +31,7 @@ export class VisualizationPanel { this._traceIndex = 0; const panel = vscode.window.createWebviewPanel( 'programflow-visualization', - 'Code Visualization', // TODO adjust name to original file name + `Visualization: ${path.basename(filePath)}`, vscode.ViewColumn.Beside, { enableScripts: true, @@ -111,11 +111,12 @@ export class VisualizationPanel { public static async getVisualizationPanel( context: vscode.ExtensionContext, + filePath: string, fileHash: string, trace: BackendTrace, tracePort: MessagePort | null ): Promise { - return new VisualizationPanel(context, fileHash, trace, tracePort); + return new VisualizationPanel(context, filePath, fileHash, trace, tracePort); } // TODO: Look if Typescript is possible OR do better documentation in all files diff --git a/src/programflow-visualization/main.ts b/src/programflow-visualization/main.ts index 5f98b13d..d798082e 100644 --- a/src/programflow-visualization/main.ts +++ b/src/programflow-visualization/main.ts @@ -29,7 +29,7 @@ export function getProgFlowVizCallback(context: vscode.ExtensionContext): () => tracePort = startBackend(context, file); } - const result = await startFrontend(context, fileHash, tracePort); + const result = await startFrontend(context, file.fsPath, fileHash, tracePort); if (result) { await vscode.window.showErrorMessage("Error ProgramFlow-Visualization: " + result.errorMessage); return; From dec2699e0d09c0277c3e3fa7a52613d8c99d8f39 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 11:33:14 +0200 Subject: [PATCH 27/43] Improve escaping values for HTML view --- .../frontend/HTMLGenerator.ts | 16 ++++++++++++---- 1 file changed, 12 insertions(+), 4 deletions(-) diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 47078f18..01e788dc 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -6,6 +6,16 @@ function escapeHTML(s: any) { } } +function toID(s: any) { + if (s !== undefined && s !== null) { + return Array.from(s.toString()).filter((c) => { + return c !== "\"" && c !== "\t" && c !== "\n" && c !== "\f" && c !== "\r" && c !== " "; + }).join(""); + } else { + return s; + } +} + export class HTMLGenerator { uniqueId: number = -1; @@ -92,8 +102,6 @@ export class HTMLGenerator { return result; } - // TODO: escape all values for embedding them into HTML - private dictValue(key: any, value: Value): string { this.uniqueId++; return ` @@ -148,11 +156,11 @@ export class HTMLGenerator { private frameSubItem(frameName: string, namedValue: NamedValue): string { return ` -
+
${escapeHTML(namedValue.name)}
-
+
${this.getCorrectValueOf(namedValue)}
From 548b40a64cdde7da7452ad7fb99c6296ebe966de Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 12:37:54 +0200 Subject: [PATCH 28/43] Move visualization logs to an OutputChannel --- src/extension.ts | 9 ++++--- .../backend/backend.ts | 19 ++++++++------ .../backend/trace_generator.ts | 26 +++++++++++-------- src/programflow-visualization/main.ts | 9 ++++--- 4 files changed, 37 insertions(+), 26 deletions(-) diff --git a/src/extension.ts b/src/extension.ts index 8ddca1fc..fa2ce9f1 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -129,10 +129,10 @@ function installCmd( installButton(buttonTitle, cmdId); } -function initProgramFlowVisualization(context: vscode.ExtensionContext) { +function initProgramFlowVisualization(context: vscode.ExtensionContext, outChannel: vscode.OutputChannel) { initTraceCache(context); const cmdId = extensionId + ".programflow-visualization"; - let disposable = vscode.commands.registerCommand(cmdId, getProgFlowVizCallback(context)); + let disposable = vscode.commands.registerCommand(cmdId, getProgFlowVizCallback(context, outChannel)); disposables.push(disposable); context.subscriptions.push(disposable); installButton("$(debug-alt-small) Visualize", cmdId); @@ -334,6 +334,9 @@ export function activate(context: vscode.ExtensionContext) { console.log('Activating extension ' + extensionId); + const outChannel = vscode.window.createOutputChannel("Write Your Python Program"); + disposables.push(outChannel); + fixPythonConfig(context); const terminals: { [name: string]: TerminalContext } = {}; @@ -395,7 +398,7 @@ export function activate(context: vscode.ExtensionContext) { } ); - initProgramFlowVisualization(context); + initProgramFlowVisualization(context, outChannel); vscode.window.onDidChangeActiveTextEditor(showHideButtons); showHideButtons(vscode.window.activeTextEditor); diff --git a/src/programflow-visualization/backend/backend.ts b/src/programflow-visualization/backend/backend.ts index 19e4017c..686770cb 100644 --- a/src/programflow-visualization/backend/backend.ts +++ b/src/programflow-visualization/backend/backend.ts @@ -1,18 +1,18 @@ -import { ExtensionContext, Uri } from 'vscode'; +import { ExtensionContext, OutputChannel, Uri } from 'vscode'; import { MessagePort, Worker } from 'worker_threads'; import { PythonExtension, getPythonCmd } from '../../extension'; import path = require('path'); -export function startBackend(context: ExtensionContext, file: Uri): MessagePort { +export function startBackend(context: ExtensionContext, file: Uri, outChannel: OutputChannel): MessagePort { const pyExt = new PythonExtension(); const pythonCmdResult = getPythonCmd(pyExt); let pythonCmd = ["python3"]; switch (pythonCmdResult.kind) { case "error": - console.error("Getting Python command failed:", pythonCmdResult.msg); + outChannel.appendLine(`Getting Python command failed: ${pythonCmdResult.msg}`); break; case "warning": - console.error("Warning while getting Python command:", pythonCmdResult.msg); + outChannel.appendLine(`Warning while getting Python command: ${pythonCmdResult.msg}`); pythonCmd = pythonCmdResult.cmd; break; case "success": @@ -26,13 +26,16 @@ export function startBackend(context: ExtensionContext, file: Uri): MessagePort const mainPath = context.asAbsolutePath("python/deps/pytrace-generator/main.py"); const workerPath = path.resolve(__dirname, 'trace_generator.js'); const worker = new Worker(workerPath); - const channel = new MessageChannel(); + const traceChannel = new MessageChannel(); + const logChannel = new MessageChannel(); + logChannel.port2.on('message', async (logEntry) => outChannel.append(logEntry)); worker.postMessage({ file: file.fsPath, mainPath: mainPath, + logPort: logChannel.port1, pythonCmd: pythonCmd, runYourProgramPath: runYourProgramPath, - tracePort: channel.port1 - }, [channel.port1]); - return channel.port2; + tracePort: traceChannel.port1 + }, [logChannel.port1, traceChannel.port1]); + return traceChannel.port2; } diff --git a/src/programflow-visualization/backend/trace_generator.ts b/src/programflow-visualization/backend/trace_generator.ts index 6c2a6a59..5fae4985 100644 --- a/src/programflow-visualization/backend/trace_generator.ts +++ b/src/programflow-visualization/backend/trace_generator.ts @@ -8,7 +8,7 @@ function installWypp(pythonCmd: string[], runYourProgramPath: string, callback: execFile(pythonCmd[0], args, { windowsHide: true }, callback); } -export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort) { +export function generateTrace(pythonCmd: string[], mainPath: string, filePath: string, tracePort: MessagePort, logPort: MessagePort) { let childRef: ChildProcessWithoutNullStreams[] = []; let buffer = Buffer.alloc(0); const server = createServer((socket) => { @@ -23,7 +23,7 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s tracePort.postMessage(traceElem); buffer = buffer.subarray(4 + dataLen); } catch (error) { - console.error('JSON parsing of trace element failed:', error); + logPort.postMessage(`JSON parsing of trace element failed: ${error}\n`); tracePort.close(); if (childRef.length > 0) { childRef[0].kill(); @@ -49,21 +49,23 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s tracePort.on('close', () => { child.kill(); + logPort.close(); }); child.stdout.on('data', (data) => { - console.log(data.toString()); + logPort.postMessage(data.toString()); }); child.stderr.on('data', (data) => { - err += data.toString(); + logPort.postMessage(data.toString()); }); child.on('close', (code) => { if (code !== 0) { - console.error(`trace generator failed with code ${code}: ${err}`); + logPort.postMessage(`trace generator failed with code ${code}: ${err}\n`); } }); child.on('error', (error) => { - console.error(error); + logPort.postMessage(`${error}\n`); + logPort.close(); tracePort.close(); }); }); @@ -72,23 +74,25 @@ export function generateTrace(pythonCmd: string[], mainPath: string, filePath: s if (!isMainThread && parentPort) { parentPort.once('message', (initParams) => { if (initParams.pythonCmd.length === 0) { - console.error("Python command is missing"); + initParams.logPort.postMessage('Python command is missing\n'); + initParams.logPort.close(); initParams.tracePort.close(); return; } installWypp(initParams.pythonCmd, initParams.runYourProgramPath, (error, stdout, stderr) => { if (stdout.length > 0) { - console.log(stdout); + initParams.logPort.postMessage(`${stdout}\n`); } if (stderr.length > 0) { - console.error(stderr); + initParams.logPort.postMessage(`${stderr}\n`); } if (error !== null) { - console.error(error); + initParams.logPort.postMessage(`${error}\n`); + initParams.logPort.close(); initParams.tracePort.close(); return; } - generateTrace(initParams.pythonCmd, initParams.mainPath, initParams.file, initParams.tracePort); + generateTrace(initParams.pythonCmd, initParams.mainPath, initParams.file, initParams.tracePort, initParams.logPort); }); }); } diff --git a/src/programflow-visualization/main.ts b/src/programflow-visualization/main.ts index d798082e..64c18d65 100644 --- a/src/programflow-visualization/main.ts +++ b/src/programflow-visualization/main.ts @@ -5,7 +5,7 @@ import { Md5 } from 'ts-md5'; import { startFrontend } from './frontend/frontend'; import { traceAlreadyExists } from './trace_cache'; -export function getProgFlowVizCallback(context: vscode.ExtensionContext): () => Promise { +export function getProgFlowVizCallback(context: vscode.ExtensionContext, outChannel: vscode.OutputChannel): () => Promise { return async () => { try { const file = vscode.window.activeTextEditor ? @@ -26,7 +26,7 @@ export function getProgFlowVizCallback(context: vscode.ExtensionContext): () => let tracePort = null; if (!(await traceAlreadyExists(context, fileHash))) { - tracePort = startBackend(context, file); + tracePort = startBackend(context, file, outChannel); } const result = await startFrontend(context, file.fsPath, fileHash, tracePort); @@ -36,10 +36,11 @@ export function getProgFlowVizCallback(context: vscode.ExtensionContext): () => } } catch (e: any) { if (e instanceof Error) { - console.log(e.stack?.toString()); + outChannel.appendLine(e.stack?.toString() ?? "Error: "); } else { - console.log(e); + outChannel.appendLine(e); } + outChannel.show(true); } }; } From c1e91e21112d23d71d0c9e8eaaeb6a6c951b50d4 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 15:03:14 +0200 Subject: [PATCH 29/43] Also show class objects in visualization --- python/deps/pytrace-generator/main.py | 2 -- 1 file changed, 2 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index e1b6e0bd..02b1a547 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -252,8 +252,6 @@ def should_ignore(variable_name, value, script_path, ignore_list = []): return True if inspect.ismodule(value): return True - if inspect.isclass(value): - return True if inspect.isframe(value): return True return False From 6ae97f075a7d89793282c631c4fbe306b3e19883 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 10 Oct 2024 16:50:04 +0200 Subject: [PATCH 30/43] Fix expected result for wyppSimple test --- .../test/test-cases/wyppSimple.py.json | 33 ++++++++++++++++++- 1 file changed, 32 insertions(+), 1 deletion(-) diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json index 9288a002..eb1a1467 100644 --- a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json +++ b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -101,7 +101,13 @@ "stack": [ { "frameName": "", - "locals": [] + "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + } + ] } ], "heap": {} @@ -113,6 +119,11 @@ { "frameName": "", "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, { "type": "ref", "value": 0, @@ -136,6 +147,11 @@ { "frameName": "", "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, { "type": "ref", "value": 0, @@ -174,6 +190,11 @@ { "frameName": "", "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, { "type": "ref", "value": 0, @@ -216,6 +237,11 @@ { "frameName": "", "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, { "type": "ref", "value": 0, @@ -269,6 +295,11 @@ { "frameName": "", "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, { "type": "ref", "value": 0, From 33f9fab42a1f65e22a55fa90ff4834cc8e4326f2 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Fri, 11 Oct 2024 13:08:46 +0200 Subject: [PATCH 31/43] Add an extra visualization step with the return value --- python/deps/pytrace-generator/main.py | 24 +++-- .../test/test-cases/factorial.py.json | 96 +++++++++++++++++++ .../test/test-cases/wyppSimple.py.json | 84 ++++++++++++++++ .../frontend/HTMLGenerator.ts | 6 +- .../frontend/resources/webview.css | 4 + 5 files changed, 205 insertions(+), 9 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 02b1a547..e6f9422e 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -267,7 +267,7 @@ def should_ignore_on_heap(variable_name, value, script_path, ignore_list = []): return False -def generate_heap(frame, script_path, ignore): +def generate_heap(frame, script_path, ignore, return_value = None): heap = Heap(script_path) while True: for variable_name in frame.f_locals: @@ -277,6 +277,11 @@ def generate_heap(frame, script_path, ignore): continue value = frame.f_locals[variable_name] heap.store(id(value), value) + + if return_value is not None: + # Store return value + if not should_ignore_on_stack("return", return_value, script_path) and not primitive_type(type(return_value)) != "ref": + heap.store(id(return_value), return_value) frame = frame.f_back if frame is None or frame.f_code.co_qualname == "Bdb.run": @@ -329,16 +334,17 @@ def trace_dispatch(self, frame, event, arg): next_source_line = linecache.getline(filename, line).strip() self.import_following = import_regex.search(next_source_line) is not None - if event == "call": - self.stack.push_frame(frame) - elif event == "return": - self.stack.pop_frame() - elif event == "line": + display_return = event == "return" and len(self.stack.frames) > 1 + if event == "line" or display_return: for variable_name in frame.f_locals: if should_ignore_on_stack(variable_name, frame.f_locals[variable_name], self.filename, self.stack_ignore): continue self.stack.push(variable_name, frame.f_locals[variable_name]) - heap = generate_heap(frame, self.filename, self.stack_ignore) + if event == "return" and display_return: + self.stack.push("return", arg) + heap = generate_heap(frame, self.filename, self.stack_ignore, return_value=arg) + else: + heap = generate_heap(frame, self.filename, self.stack_ignore) step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) @@ -348,6 +354,10 @@ def trace_dispatch(self, frame, event, arg): json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) self.trace_socket.sendall(json_len) self.trace_socket.sendall(json_str) + if event == "call": + self.stack.push_frame(frame) + elif event == "return": + self.stack.pop_frame() # TODO exception return self.trace_dispatch diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py.json b/python/deps/pytrace-generator/test/test-cases/factorial.py.json index 66a9a324..3e3e592f 100644 --- a/python/deps/pytrace-generator/test/test-cases/factorial.py.json +++ b/python/deps/pytrace-generator/test/test-cases/factorial.py.json @@ -185,6 +185,54 @@ } } }, + { + "line": 3, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + } + ] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 1, + "name": "n" + }, + { + "type": "int", + "value": 1, + "name": "return" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } + }, { "line": 5, "filePath": "factorial.py", @@ -266,6 +314,54 @@ } } }, + { + "line": 6, + "filePath": "factorial.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "ref", + "value": 0, + "name": "factorical" + } + ] + }, + { + "frameName": "factorical", + "locals": [ + { + "type": "int", + "value": 2, + "name": "n" + }, + { + "type": "int", + "value": 1, + "name": "prev" + }, + { + "type": "int", + "value": 2, + "name": "a" + }, + { + "type": "int", + "value": 2, + "name": "return" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + } + } + }, { "line": 10, "filePath": "factorial.py", diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json index eb1a1467..2119c583 100644 --- a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json +++ b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -73,6 +73,27 @@ ], "heap": {} }, + { + "line": 6, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [] + }, + { + "frameName": "Bar", + "locals": [ + { + "type": "none", + "value": null, + "name": "return" + } + ] + } + ], + "heap": {} + }, { "line": 4, "filePath": "wyppSimple.py", @@ -230,6 +251,69 @@ } } }, + { + "line": 9, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + } + ] + }, + { + "frameName": "generate_bar", + "locals": [ + { + "type": "ref", + "value": 2, + "name": "return" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + }, + "2": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + } + }, { "line": 14, "filePath": "wyppSimple.py", diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 01e788dc..d4a31c91 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -155,10 +155,12 @@ export class HTMLGenerator { } private frameSubItem(frameName: string, namedValue: NamedValue): string { + const isReturn = namedValue.name === 'return'; + const displayName = isReturn ? 'Return value' : namedValue.name; return `
-
- ${escapeHTML(namedValue.name)} +
+ ${escapeHTML(displayName)}
${this.getCorrectValueOf(namedValue)} diff --git a/src/programflow-visualization/frontend/resources/webview.css b/src/programflow-visualization/frontend/resources/webview.css index a84176ce..6c64bc41 100644 --- a/src/programflow-visualization/frontend/resources/webview.css +++ b/src/programflow-visualization/frontend/resources/webview.css @@ -163,3 +163,7 @@ body { .object-item { margin-bottom: 15px; } + +.return-value { + color: red; +} From 3919fbd763180a5805bdf80556cf52d16a6a8dd2 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 16 Oct 2024 15:19:27 +0200 Subject: [PATCH 32/43] Shorten visualization of class definitions --- python/deps/pytrace-generator/main.py | 47 +++++++++-- .../test/test-cases/wyppSimple.py.json | 84 ------------------- 2 files changed, 41 insertions(+), 90 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index e6f9422e..7a8ba91c 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -290,6 +290,23 @@ def generate_heap(frame, script_path, ignore, return_value = None): return heap +class ShownClassDefs: + def __init__(self): + self.frames = [] + + def did_show(self, filename, line): + return (filename, line) in self.frames[-1] + + def append(self, filename, line): + self.frames[-1].append((filename, line)) + + def push_frame(self): + self.frames.append([]) + + def pop_frame(self): + self.frames.pop() + + class PyTraceGenerator(bdb.Bdb): def __init__(self, trace_socket): super().__init__() @@ -300,6 +317,9 @@ def __init__(self, trace_socket): self.filename = "" self.skip_until = None self.import_following = False + self.last_step_was_class = False + self.prev_num_frames = 0 + self.shown_class_defs = ShownClassDefs() def trace_dispatch(self, frame, event, arg): filename = frame.f_code.co_filename @@ -348,16 +368,31 @@ def trace_dispatch(self, frame, event, arg): step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) - # Output trace - if self.trace_socket is not None: - json_str = json.dumps(step.format()).encode('utf-8') - json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) - self.trace_socket.sendall(json_len) - self.trace_socket.sendall(json_str) + is_annotation = next_source_line.startswith("@") + should_display_step = not is_annotation + is_class_def = next_source_line.startswith("class ") + class_def_already_shown = is_class_def and self.shown_class_defs.did_show(filename, line) + should_display_step = should_display_step and not class_def_already_shown + num_frames = len(self.stack.frames) + is_inside_class_def = num_frames > self.prev_num_frames and self.last_step_was_class + should_display_step = should_display_step and not is_inside_class_def + if should_display_step: + # Output trace + if self.trace_socket is not None: + json_str = json.dumps(step.format()).encode('utf-8') + json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) + self.trace_socket.sendall(json_len) + self.trace_socket.sendall(json_str) + if is_class_def: + self.shown_class_defs.append(filename, line) + self.last_step_was_class = is_class_def + self.prev_num_frames = num_frames if event == "call": self.stack.push_frame(frame) + self.shown_class_defs.push_frame() elif event == "return": self.stack.pop_frame() + self.shown_class_defs.pop_frame() # TODO exception return self.trace_dispatch diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json index 2119c583..526a47d9 100644 --- a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json +++ b/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -21,90 +21,6 @@ ], "heap": {} }, - { - "line": 4, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - } - ], - "heap": {} - }, - { - "line": 5, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - } - ], - "heap": {} - }, - { - "line": 4, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - }, - { - "frameName": "Bar", - "locals": [] - } - ], - "heap": {} - }, - { - "line": 6, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - }, - { - "frameName": "Bar", - "locals": [] - } - ], - "heap": {} - }, - { - "line": 6, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - }, - { - "frameName": "Bar", - "locals": [ - { - "type": "none", - "value": null, - "name": "return" - } - ] - } - ], - "heap": {} - }, - { - "line": 4, - "filePath": "wyppSimple.py", - "stack": [ - { - "frameName": "", - "locals": [] - } - ], - "heap": {} - }, { "line": 5, "filePath": "wyppSimple.py", From 5d7d503635d59135cff0e04983652febedf1e3bf Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 16 Oct 2024 15:21:42 +0200 Subject: [PATCH 33/43] Trim trailing whitespace --- python/deps/pytrace-generator/main.py | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/python/deps/pytrace-generator/main.py b/python/deps/pytrace-generator/main.py index 7a8ba91c..e49e5cd4 100644 --- a/python/deps/pytrace-generator/main.py +++ b/python/deps/pytrace-generator/main.py @@ -128,13 +128,13 @@ def push(self, variable_name, value): if stack_value.variable_name == variable_name: value_index = idx break - + stack_value = PrimitiveValue(value, variable_name) if value_index is not None: self.values[value_index] = stack_value else: self.values.append(stack_value) - + def format(self): return { "frameName": self.name, @@ -144,10 +144,10 @@ def format(self): class Stack: def __init__(self): self.frames = [] - + def push_frame(self, frame): self.frames.append(StackFrame(frame.f_code.co_qualname)) - + def pop_frame(self): self.frames.pop() @@ -168,7 +168,7 @@ class Heap: def __init__(self, script_path): self.script_path = script_path self.memory = {} - + def store(self, address, value): # This check only works because we are taking heap snapshots if address in self.memory: @@ -277,7 +277,7 @@ def generate_heap(frame, script_path, ignore, return_value = None): continue value = frame.f_locals[variable_name] heap.store(id(value), value) - + if return_value is not None: # Store return value if not should_ignore_on_stack("return", return_value, script_path) and not primitive_type(type(return_value)) != "ref": @@ -286,7 +286,7 @@ def generate_heap(frame, script_path, ignore, return_value = None): frame = frame.f_back if frame is None or frame.f_code.co_qualname == "Bdb.run": break - + return heap @@ -401,7 +401,7 @@ def run_script(self, filename, script_str): self.filename = filename code = compile(script_str, self.filename, "exec") self.run(code) - + if len(sys.argv) <= 1: eprint("not enough arguments") From 20b7c966da112e6cd15c619f166483c40d257532 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 16 Oct 2024 17:38:30 +0200 Subject: [PATCH 34/43] Run pytrace-generator tests in CI --- .github/workflows/github-action-test-python.yml | 2 ++ 1 file changed, 2 insertions(+) diff --git a/.github/workflows/github-action-test-python.yml b/.github/workflows/github-action-test-python.yml index 94b5f8d6..a38f4cf8 100644 --- a/.github/workflows/github-action-test-python.yml +++ b/.github/workflows/github-action-test-python.yml @@ -22,3 +22,5 @@ jobs: - name: Test run: | cd python && ./allTestsForPyVersion + - name: Test pytrace-generator + run: python3 python/deps/pytrace-generator/test/runTests.py From 3d6b5990c2be5768f5ab3ffdcc52f30bceb767ee Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Wed, 16 Oct 2024 17:44:21 +0200 Subject: [PATCH 35/43] CI: install wypp before testing pytrace-generator --- .github/workflows/github-action-test-python.yml | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/.github/workflows/github-action-test-python.yml b/.github/workflows/github-action-test-python.yml index a38f4cf8..100c6bc6 100644 --- a/.github/workflows/github-action-test-python.yml +++ b/.github/workflows/github-action-test-python.yml @@ -23,4 +23,6 @@ jobs: run: | cd python && ./allTestsForPyVersion - name: Test pytrace-generator - run: python3 python/deps/pytrace-generator/test/runTests.py + run: | + python3 python/src/runYourProgram.py --install-mode installOnly + python3 python/deps/pytrace-generator/test/runTests.py From c72e275ba33aa971aa1d2c53467bfe0622dca894 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 17 Oct 2024 16:37:29 +0200 Subject: [PATCH 36/43] Drop support for Python versions < 3.12 --- .github/workflows/github-action-test-python.yml | 2 +- README.md | 2 +- python/allTests | 2 -- 3 files changed, 2 insertions(+), 4 deletions(-) diff --git a/.github/workflows/github-action-test-python.yml b/.github/workflows/github-action-test-python.yml index 100c6bc6..79ff711e 100644 --- a/.github/workflows/github-action-test-python.yml +++ b/.github/workflows/github-action-test-python.yml @@ -12,7 +12,7 @@ jobs: strategy: matrix: # You need to change to branch protection rules if you change the versions here - python-version: [3.10.13, 3.11.0, 3.11.7, 3.12.0, 3.12.1] + python-version: [3.12.0, 3.12.1] steps: - uses: actions/checkout@v2 - name: Set up Python ${{ matrix.python-version }} diff --git a/README.md b/README.md index 92a51de3..96dd4f6a 100644 --- a/README.md +++ b/README.md @@ -10,7 +10,7 @@ The ideas of this environment are based on the great ideas from ## Quick start -* Step 1. Install **Python 3.10.x** or **Python 3.11.x** or **Python 3.12.x**. +* Step 1. Install **Python 3.12.x**. * Step 2. Install the **write-your-python-program** extension. You need at least Visual Studio Code version 1.85.0. * Step 3. Open or create a Python file. The "RUN" button in the taskbar at the bottom will diff --git a/python/allTests b/python/allTests index e5b6a276..8d3e4b22 100755 --- a/python/allTests +++ b/python/allTests @@ -15,6 +15,4 @@ function run() echo } -PYENV_VERSION=3.10.13 run -PYENV_VERSION=3.11.7 run PYENV_VERSION=3.12.1 run From 5c7146de73d07556a133b9accf75c16145491520 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 17 Oct 2024 16:40:16 +0200 Subject: [PATCH 37/43] Move pytrace-generator to root directory --- .github/workflows/github-action-test-python.yml | 2 +- {python/deps/pytrace-generator => pytrace-generator}/main.py | 0 .../test/generateExpectation.py | 0 .../pytrace-generator => pytrace-generator}/test/runTests.py | 0 .../test/test-cases/empty.py | 0 .../test/test-cases/empty.py.json | 0 .../test/test-cases/factorial.py | 0 .../test/test-cases/factorial.py.json | 0 .../test/test-cases/generatorObject.py | 0 .../test/test-cases/importArgparse.py | 0 .../test/test-cases/importArgparse.py.json | 0 .../test/test-cases/importShutil.py | 0 .../test/test-cases/importShutil.py.json | 0 .../test/test-cases/importSubprocess.py | 0 .../test/test-cases/importSubprocess.py.json | 0 .../test/test-cases/importTime.py | 0 .../test/test-cases/importTime.py.json | 0 .../test/test-cases/importWypp.py | 0 .../test/test-cases/importWypp.py.json | 0 .../test/test-cases/module1.py | 0 .../test/test-cases/module2.py | 0 .../test/test-cases/wyppSimple.py | 0 .../test/test-cases/wyppSimple.py.json | 0 .../deps/pytrace-generator => pytrace-generator}/test/util.py | 0 src/programflow-visualization/backend/backend.ts | 2 +- 25 files changed, 2 insertions(+), 2 deletions(-) rename {python/deps/pytrace-generator => pytrace-generator}/main.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/generateExpectation.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/runTests.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/empty.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/empty.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/factorial.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/factorial.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/generatorObject.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importArgparse.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importArgparse.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importShutil.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importShutil.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importSubprocess.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importSubprocess.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importTime.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importTime.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importWypp.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/importWypp.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/module1.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/module2.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/wyppSimple.py (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/test-cases/wyppSimple.py.json (100%) rename {python/deps/pytrace-generator => pytrace-generator}/test/util.py (100%) diff --git a/.github/workflows/github-action-test-python.yml b/.github/workflows/github-action-test-python.yml index 79ff711e..9102b96e 100644 --- a/.github/workflows/github-action-test-python.yml +++ b/.github/workflows/github-action-test-python.yml @@ -25,4 +25,4 @@ jobs: - name: Test pytrace-generator run: | python3 python/src/runYourProgram.py --install-mode installOnly - python3 python/deps/pytrace-generator/test/runTests.py + python3 pytrace-generator/test/runTests.py diff --git a/python/deps/pytrace-generator/main.py b/pytrace-generator/main.py similarity index 100% rename from python/deps/pytrace-generator/main.py rename to pytrace-generator/main.py diff --git a/python/deps/pytrace-generator/test/generateExpectation.py b/pytrace-generator/test/generateExpectation.py similarity index 100% rename from python/deps/pytrace-generator/test/generateExpectation.py rename to pytrace-generator/test/generateExpectation.py diff --git a/python/deps/pytrace-generator/test/runTests.py b/pytrace-generator/test/runTests.py similarity index 100% rename from python/deps/pytrace-generator/test/runTests.py rename to pytrace-generator/test/runTests.py diff --git a/python/deps/pytrace-generator/test/test-cases/empty.py b/pytrace-generator/test/test-cases/empty.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/empty.py rename to pytrace-generator/test/test-cases/empty.py diff --git a/python/deps/pytrace-generator/test/test-cases/empty.py.json b/pytrace-generator/test/test-cases/empty.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/empty.py.json rename to pytrace-generator/test/test-cases/empty.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py b/pytrace-generator/test/test-cases/factorial.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/factorial.py rename to pytrace-generator/test/test-cases/factorial.py diff --git a/python/deps/pytrace-generator/test/test-cases/factorial.py.json b/pytrace-generator/test/test-cases/factorial.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/factorial.py.json rename to pytrace-generator/test/test-cases/factorial.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/generatorObject.py b/pytrace-generator/test/test-cases/generatorObject.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/generatorObject.py rename to pytrace-generator/test/test-cases/generatorObject.py diff --git a/python/deps/pytrace-generator/test/test-cases/importArgparse.py b/pytrace-generator/test/test-cases/importArgparse.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importArgparse.py rename to pytrace-generator/test/test-cases/importArgparse.py diff --git a/python/deps/pytrace-generator/test/test-cases/importArgparse.py.json b/pytrace-generator/test/test-cases/importArgparse.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importArgparse.py.json rename to pytrace-generator/test/test-cases/importArgparse.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/importShutil.py b/pytrace-generator/test/test-cases/importShutil.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importShutil.py rename to pytrace-generator/test/test-cases/importShutil.py diff --git a/python/deps/pytrace-generator/test/test-cases/importShutil.py.json b/pytrace-generator/test/test-cases/importShutil.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importShutil.py.json rename to pytrace-generator/test/test-cases/importShutil.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py b/pytrace-generator/test/test-cases/importSubprocess.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importSubprocess.py rename to pytrace-generator/test/test-cases/importSubprocess.py diff --git a/python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json b/pytrace-generator/test/test-cases/importSubprocess.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importSubprocess.py.json rename to pytrace-generator/test/test-cases/importSubprocess.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py b/pytrace-generator/test/test-cases/importTime.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importTime.py rename to pytrace-generator/test/test-cases/importTime.py diff --git a/python/deps/pytrace-generator/test/test-cases/importTime.py.json b/pytrace-generator/test/test-cases/importTime.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importTime.py.json rename to pytrace-generator/test/test-cases/importTime.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/importWypp.py b/pytrace-generator/test/test-cases/importWypp.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importWypp.py rename to pytrace-generator/test/test-cases/importWypp.py diff --git a/python/deps/pytrace-generator/test/test-cases/importWypp.py.json b/pytrace-generator/test/test-cases/importWypp.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/importWypp.py.json rename to pytrace-generator/test/test-cases/importWypp.py.json diff --git a/python/deps/pytrace-generator/test/test-cases/module1.py b/pytrace-generator/test/test-cases/module1.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/module1.py rename to pytrace-generator/test/test-cases/module1.py diff --git a/python/deps/pytrace-generator/test/test-cases/module2.py b/pytrace-generator/test/test-cases/module2.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/module2.py rename to pytrace-generator/test/test-cases/module2.py diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py b/pytrace-generator/test/test-cases/wyppSimple.py similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/wyppSimple.py rename to pytrace-generator/test/test-cases/wyppSimple.py diff --git a/python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json b/pytrace-generator/test/test-cases/wyppSimple.py.json similarity index 100% rename from python/deps/pytrace-generator/test/test-cases/wyppSimple.py.json rename to pytrace-generator/test/test-cases/wyppSimple.py.json diff --git a/python/deps/pytrace-generator/test/util.py b/pytrace-generator/test/util.py similarity index 100% rename from python/deps/pytrace-generator/test/util.py rename to pytrace-generator/test/util.py diff --git a/src/programflow-visualization/backend/backend.ts b/src/programflow-visualization/backend/backend.ts index 686770cb..cdba9bd5 100644 --- a/src/programflow-visualization/backend/backend.ts +++ b/src/programflow-visualization/backend/backend.ts @@ -23,7 +23,7 @@ export function startBackend(context: ExtensionContext, file: Uri, outChannel: O // Get WYPP path const runYourProgramPath = context.asAbsolutePath("python/src/runYourProgram.py"); - const mainPath = context.asAbsolutePath("python/deps/pytrace-generator/main.py"); + const mainPath = context.asAbsolutePath("pytrace-generator/main.py"); const workerPath = path.resolve(__dirname, 'trace_generator.js'); const worker = new Worker(workerPath); const traceChannel = new MessageChannel(); From 330c5ccaed210cb44413ebbb8dfa7f8e0d51bf1e Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 21 Oct 2024 20:06:14 +0200 Subject: [PATCH 38/43] Ignore pytrace-generator tests on distribution --- .vscodeignore | 1 + 1 file changed, 1 insertion(+) diff --git a/.vscodeignore b/.vscodeignore index 37785fda..3185608a 100644 --- a/.vscodeignore +++ b/.vscodeignore @@ -15,3 +15,4 @@ python/deps/untypy/examples/** python/tests/* python/file-tests/* python/integration-tests/* +pytrace-generator/test/** From 0edfeaf00e5bee6a921f1ed48d28f5e343f07452 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 21 Oct 2024 20:19:25 +0200 Subject: [PATCH 39/43] Add stdout to visualization --- pytrace-generator/main.py | 20 ++- .../test/test-cases/empty.py.json | 3 +- .../test/test-cases/factorial.py.json | 33 +++-- .../test/test-cases/importArgparse.py.json | 6 +- .../test/test-cases/importShutil.py.json | 6 +- .../test/test-cases/importSubprocess.py.json | 6 +- .../test/test-cases/importTime.py.json | 6 +- .../test/test-cases/importWypp.py.json | 6 +- .../test/test-cases/wyppSimple.py | 2 + .../test/test-cases/wyppSimple.py.json | 133 ++++++++++++++++-- .../frontend/HTMLGenerator.ts | 2 +- .../frontend/resources/webview.css | 27 +++- .../frontend/resources/webview.js | 3 + .../frontend/visualization_panel.ts | 35 +++-- src/programflow-visualization/types.ts | 3 +- 15 files changed, 237 insertions(+), 54 deletions(-) diff --git a/pytrace-generator/main.py b/pytrace-generator/main.py index e49e5cd4..ce348097 100644 --- a/pytrace-generator/main.py +++ b/pytrace-generator/main.py @@ -2,6 +2,7 @@ import copy from dataclasses import dataclass import inspect +import io import json import linecache import math @@ -235,13 +236,15 @@ class TraceStep: file_path: str stack: Stack heap: Heap + stdout: str def format(self): return { "line": self.line, "filePath": self.file_path, "stack": self.stack.format(), - "heap": self.heap.format() + "heap": self.heap.format(), + "stdout": self.stdout, } @@ -320,6 +323,8 @@ def __init__(self, trace_socket): self.last_step_was_class = False self.prev_num_frames = 0 self.shown_class_defs = ShownClassDefs() + self.accumulated_stdout = "" + self.captured_stdout = io.StringIO() def trace_dispatch(self, frame, event, arg): filename = frame.f_code.co_filename @@ -365,8 +370,9 @@ def trace_dispatch(self, frame, event, arg): heap = generate_heap(frame, self.filename, self.stack_ignore, return_value=arg) else: heap = generate_heap(frame, self.filename, self.stack_ignore) + accumulated_stdout = self.accumulated_stdout + self.captured_stdout.getvalue() - step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap)) + step = TraceStep(line, filename, copy.deepcopy(self.stack), copy.deepcopy(heap), accumulated_stdout) is_annotation = next_source_line.startswith("@") should_display_step = not is_annotation @@ -383,6 +389,8 @@ def trace_dispatch(self, frame, event, arg): json_len = len(json_str).to_bytes(4, byteorder='big', signed=False) self.trace_socket.sendall(json_len) self.trace_socket.sendall(json_str) + self.accumulated_stdout = accumulated_stdout + self.clear_stdout_capture() if is_class_def: self.shown_class_defs.append(filename, line) self.last_step_was_class = is_class_def @@ -397,10 +405,18 @@ def trace_dispatch(self, frame, event, arg): return self.trace_dispatch + def clear_stdout_capture(self): + self.captured_stdout.close() + self.captured_stdout = io.StringIO() + sys.stdout = self.captured_stdout + def run_script(self, filename, script_str): self.filename = filename code = compile(script_str, self.filename, "exec") + real_stdout = sys.stdout + sys.stdout = self.captured_stdout self.run(code) + sys.stdout = real_stdout if len(sys.argv) <= 1: diff --git a/pytrace-generator/test/test-cases/empty.py.json b/pytrace-generator/test/test-cases/empty.py.json index 573e9b11..8c5ac30e 100644 --- a/pytrace-generator/test/test-cases/empty.py.json +++ b/pytrace-generator/test/test-cases/empty.py.json @@ -8,6 +8,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/factorial.py.json b/pytrace-generator/test/test-cases/factorial.py.json index 3e3e592f..7b1c40fd 100644 --- a/pytrace-generator/test/test-cases/factorial.py.json +++ b/pytrace-generator/test/test-cases/factorial.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 8, @@ -31,7 +32,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 2, @@ -64,7 +66,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 4, @@ -97,7 +100,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 2, @@ -140,7 +144,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 3, @@ -183,7 +188,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 3, @@ -231,7 +237,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 5, @@ -269,7 +276,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 6, @@ -312,7 +320,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 6, @@ -360,7 +369,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 10, @@ -388,6 +398,7 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/importArgparse.py.json b/pytrace-generator/test/test-cases/importArgparse.py.json index a8d44894..ba9d8d2b 100644 --- a/pytrace-generator/test/test-cases/importArgparse.py.json +++ b/pytrace-generator/test/test-cases/importArgparse.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 3, @@ -19,6 +20,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/importShutil.py.json b/pytrace-generator/test/test-cases/importShutil.py.json index e8b5da02..e6a09559 100644 --- a/pytrace-generator/test/test-cases/importShutil.py.json +++ b/pytrace-generator/test/test-cases/importShutil.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 3, @@ -19,6 +20,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/importSubprocess.py.json b/pytrace-generator/test/test-cases/importSubprocess.py.json index 25cc564c..d650433f 100644 --- a/pytrace-generator/test/test-cases/importSubprocess.py.json +++ b/pytrace-generator/test/test-cases/importSubprocess.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 3, @@ -19,6 +20,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/importTime.py.json b/pytrace-generator/test/test-cases/importTime.py.json index 873d3fe0..e9102f0b 100644 --- a/pytrace-generator/test/test-cases/importTime.py.json +++ b/pytrace-generator/test/test-cases/importTime.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 2, @@ -19,6 +20,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/importWypp.py.json b/pytrace-generator/test/test-cases/importWypp.py.json index 69f974f8..78720365 100644 --- a/pytrace-generator/test/test-cases/importWypp.py.json +++ b/pytrace-generator/test/test-cases/importWypp.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 2, @@ -19,6 +20,7 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" } ] \ No newline at end of file diff --git a/pytrace-generator/test/test-cases/wyppSimple.py b/pytrace-generator/test/test-cases/wyppSimple.py index edeb2569..8f2e9820 100644 --- a/pytrace-generator/test/test-cases/wyppSimple.py +++ b/pytrace-generator/test/test-cases/wyppSimple.py @@ -9,6 +9,8 @@ def generate_bar(): return Bar("baz value") obj1 = Bar("baz value") +print(obj1.baz) obj2 = generate_bar() +print(obj2.baz) check(obj1, obj2) diff --git a/pytrace-generator/test/test-cases/wyppSimple.py.json b/pytrace-generator/test/test-cases/wyppSimple.py.json index 526a47d9..2ecf1e30 100644 --- a/pytrace-generator/test/test-cases/wyppSimple.py.json +++ b/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -8,7 +8,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 2, @@ -19,7 +20,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 5, @@ -30,7 +32,8 @@ "locals": [] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 8, @@ -47,7 +50,8 @@ ] } ], - "heap": {} + "heap": {}, + "stdout": "" }, { "line": 11, @@ -75,7 +79,8 @@ "value": {}, "name": "function" } - } + }, + "stdout": "" }, { "line": 12, @@ -118,7 +123,52 @@ }, "name": "Bar" } - } + }, + "stdout": "" + }, + { + "line": 13, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + }, + "stdout": "baz value\n" }, { "line": 9, @@ -165,7 +215,8 @@ }, "name": "Bar" } - } + }, + "stdout": "baz value\n" }, { "line": 9, @@ -228,7 +279,8 @@ }, "name": "Bar" } - } + }, + "stdout": "baz value\n" }, { "line": 14, @@ -286,7 +338,8 @@ }, "name": "Bar" } - } + }, + "stdout": "baz value\n" }, { "line": 16, @@ -344,6 +397,66 @@ }, "name": "Bar" } - } + }, + "stdout": "baz value\nbaz value\n" + }, + { + "line": 18, + "filePath": "wyppSimple.py", + "stack": [ + { + "frameName": "", + "locals": [ + { + "type": "type", + "value": "", + "name": "Bar" + }, + { + "type": "ref", + "value": 0, + "name": "generate_bar" + }, + { + "type": "ref", + "value": 1, + "name": "obj1" + }, + { + "type": "ref", + "value": 2, + "name": "obj2" + } + ] + } + ], + "heap": { + "0": { + "type": "instance", + "value": {}, + "name": "function" + }, + "1": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + }, + "2": { + "type": "instance", + "value": { + "baz": { + "type": "str", + "value": "baz value" + } + }, + "name": "Bar" + } + }, + "stdout": "baz value\nbaz value\n" } ] \ No newline at end of file diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index d4a31c91..7a6360c2 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -38,7 +38,7 @@ export class HTMLGenerator { ${keys.map((name, index) => this.objectItem(name, values[index])).join('')}
`; - return [traceElement.line, frameItems, objectItems, traceElement.filePath]; + return [traceElement.line, frameItems, objectItems, traceElement.filePath, traceElement.stdout]; } private objectItem(name: string, value: HeapValue): string { diff --git a/src/programflow-visualization/frontend/resources/webview.css b/src/programflow-visualization/frontend/resources/webview.css index 6c64bc41..14c8bf90 100644 --- a/src/programflow-visualization/frontend/resources/webview.css +++ b/src/programflow-visualization/frontend/resources/webview.css @@ -1,8 +1,9 @@ body { - padding: 15px; - height: fit-content; - width: 100%; - overflow: hidden; + height: 100vh; + width: 100vw; + overflow: auto; + display: flex; + flex-direction: column; } * { @@ -38,6 +39,24 @@ body { overflow: scroll; } +#viz { + flex-grow: 1; + padding-top: 10px; +} + +#bottom-area { + min-height: 160px; + max-height: 160px; + padding-bottom: 10px; +} + +#stdout-log { + height: 100%; + border: 1.5px; + border-style: solid; + border-color: grey; +} + .next-line-color { border: 6px solid rgba(255, 255, 0, 1); border-radius: 8px; diff --git a/src/programflow-visualization/frontend/resources/webview.js b/src/programflow-visualization/frontend/resources/webview.js index 98fef2eb..f939f3b0 100644 --- a/src/programflow-visualization/frontend/resources/webview.js +++ b/src/programflow-visualization/frontend/resources/webview.js @@ -67,6 +67,9 @@ function updateVisualization(traceElem) { document.getElementById("viz").addEventListener("scroll", () => { updateRefArrows(traceElem); }); + const stdoutLog = document.getElementById("stdout-log"); + stdoutLog.innerHTML = traceElem[4]; + stdoutLog.scrollTo(0, stdoutLog.scrollHeight); } /** diff --git a/src/programflow-visualization/frontend/visualization_panel.ts b/src/programflow-visualization/frontend/visualization_panel.ts index 87cee1cd..5d564fd9 100644 --- a/src/programflow-visualization/frontend/visualization_panel.ts +++ b/src/programflow-visualization/frontend/visualization_panel.ts @@ -132,7 +132,7 @@ export class VisualizationPanel { Code Visualization - +
@@ -151,19 +151,26 @@ export class VisualizationPanel {
-
- -
-
-

Step 

-

0

-

/?

-
-
- - - - +
+
+
+ +
+
+

Step 

+

0

+

/?

+
+
+ + + + +
+
+
+

+          
         
diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts index b91a452e..0abd8352 100644 --- a/src/programflow-visualization/types.ts +++ b/src/programflow-visualization/types.ts @@ -7,7 +7,7 @@ type Failure = { errorMessage: string }; // State Types for the Frontend type FrontendTrace = Array; -type FrontendTraceElem = [number, string, string, string]; +type FrontendTraceElem = [number, string, string, string, string]; // ############################################################################################ // State Types for the Backend type PartialBackendTrace = { @@ -20,6 +20,7 @@ type BackendTraceElem = { filePath: string, stack: Array; heap: Map; + stdout: string; }; type Address = number; From 381c45abe9d9613bd44c53c801d343fde9fb39b1 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Mon, 21 Oct 2024 20:43:47 +0200 Subject: [PATCH 40/43] Visualize types on heap --- pytrace-generator/main.py | 11 +- .../test/test-cases/wyppSimple.py.json | 135 +++++++++++------- .../frontend/HTMLGenerator.ts | 11 ++ src/programflow-visualization/types.ts | 1 + 4 files changed, 105 insertions(+), 53 deletions(-) diff --git a/pytrace-generator/main.py b/pytrace-generator/main.py index ce348097..ec0bd75d 100644 --- a/pytrace-generator/main.py +++ b/pytrace-generator/main.py @@ -26,10 +26,10 @@ def eprint(*args, **kwargs): float: "float", bool: "bool", str: "str", - type(None): "none", - type: "type" + type(None): "none" } HEAP_TYPES = { + type: "type", list: "list", tuple: "tuple", dict: "dict", @@ -112,8 +112,6 @@ def format(self): "type": self.type_str, "value": self.value } - if type(d["value"]) == type: - d["value"] = str(d["value"]) if self.variable_name is not None: d["name"] = self.variable_name return d @@ -202,6 +200,11 @@ def store(self, address, value): stored_value[key_id] = prim_value if prim_value.is_ref(): inner_values.append(v) + elif value_type == type: + stored_value = str(value) + search_result = type_name_regex.search(stored_value) + if search_result is not None: + stored_value = f"" elif inspect.isgenerator(value): stored_value = {} else: diff --git a/pytrace-generator/test/test-cases/wyppSimple.py.json b/pytrace-generator/test/test-cases/wyppSimple.py.json index 2ecf1e30..fdafe8c1 100644 --- a/pytrace-generator/test/test-cases/wyppSimple.py.json +++ b/pytrace-generator/test/test-cases/wyppSimple.py.json @@ -43,14 +43,19 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" } ] } ], - "heap": {}, + "heap": { + "0": { + "type": "type", + "value": "" + } + }, "stdout": "" }, { @@ -61,13 +66,13 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" } ] @@ -75,6 +80,10 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" @@ -90,18 +99,18 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" } ] @@ -109,11 +118,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -134,18 +147,18 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" } ] @@ -153,11 +166,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -178,18 +195,18 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" } ] @@ -201,11 +218,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -226,18 +247,18 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" } ] @@ -247,7 +268,7 @@ "locals": [ { "type": "ref", - "value": 2, + "value": 3, "name": "return" } ] @@ -255,11 +276,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -269,7 +294,7 @@ }, "name": "Bar" }, - "2": { + "3": { "type": "instance", "value": { "baz": { @@ -290,23 +315,23 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" }, { "type": "ref", - "value": 2, + "value": 3, "name": "obj2" } ] @@ -314,11 +339,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -328,7 +357,7 @@ }, "name": "Bar" }, - "2": { + "3": { "type": "instance", "value": { "baz": { @@ -349,23 +378,23 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" }, { "type": "ref", - "value": 2, + "value": 3, "name": "obj2" } ] @@ -373,11 +402,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -387,7 +420,7 @@ }, "name": "Bar" }, - "2": { + "3": { "type": "instance", "value": { "baz": { @@ -408,23 +441,23 @@ "frameName": "", "locals": [ { - "type": "type", - "value": "", + "type": "ref", + "value": 0, "name": "Bar" }, { "type": "ref", - "value": 0, + "value": 1, "name": "generate_bar" }, { "type": "ref", - "value": 1, + "value": 2, "name": "obj1" }, { "type": "ref", - "value": 2, + "value": 3, "name": "obj2" } ] @@ -432,11 +465,15 @@ ], "heap": { "0": { + "type": "type", + "value": "" + }, + "1": { "type": "instance", "value": {}, "name": "function" }, - "1": { + "2": { "type": "instance", "value": { "baz": { @@ -446,7 +483,7 @@ }, "name": "Bar" }, - "2": { + "3": { "type": "instance", "value": { "baz": { diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 7a6360c2..ba5211a3 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -47,6 +47,11 @@ export class HTMLGenerator { case 'instance': headline = value.name; break; + case 'type': + // Types are displayed in the same way as function objects. + // This is simply done for consistency, even if it's not quite correct. + headline = value.value; + break; default: headline = value.type; } @@ -90,6 +95,12 @@ export class HTMLGenerator {
`; break; + case 'type': + result = ` +
+
+ `; + break; /* tuple, list, int[], int[][], ...*/ default: result = ` diff --git a/src/programflow-visualization/types.ts b/src/programflow-visualization/types.ts index 0abd8352..c1d503f1 100644 --- a/src/programflow-visualization/types.ts +++ b/src/programflow-visualization/types.ts @@ -48,6 +48,7 @@ type HeapValue = | { type: 'tuple'; value: Array } | { type: 'set'; value: Array } | { type: 'dict'; keys: Map, value: Map } + | { type: 'type', value: string } | { type: 'instance'; name: string, value: Map }; // wrapper type -> frontend list elements dodge From eafeefe130ef9f2b78d12902c836ea2ec22028e0 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 24 Oct 2024 14:38:30 +0200 Subject: [PATCH 41/43] Change visualization of return value --- src/programflow-visualization/frontend/HTMLGenerator.ts | 3 +-- src/programflow-visualization/frontend/resources/webview.css | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index ba5211a3..54231a07 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -167,11 +167,10 @@ export class HTMLGenerator { private frameSubItem(frameName: string, namedValue: NamedValue): string { const isReturn = namedValue.name === 'return'; - const displayName = isReturn ? 'Return value' : namedValue.name; return `
- ${escapeHTML(displayName)} + ${escapeHTML(namedValue.name)}
${this.getCorrectValueOf(namedValue)} diff --git a/src/programflow-visualization/frontend/resources/webview.css b/src/programflow-visualization/frontend/resources/webview.css index 14c8bf90..c374f365 100644 --- a/src/programflow-visualization/frontend/resources/webview.css +++ b/src/programflow-visualization/frontend/resources/webview.css @@ -184,5 +184,5 @@ body { } .return-value { - color: red; + color: blue; } From f2519183894aa2a7da1ee161eb00c070241137c4 Mon Sep 17 00:00:00 2001 From: Hannes Braun Date: Thu, 24 Oct 2024 14:39:50 +0200 Subject: [PATCH 42/43] Automatically scroll to bottom of visualization on update --- src/programflow-visualization/frontend/resources/webview.js | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/programflow-visualization/frontend/resources/webview.js b/src/programflow-visualization/frontend/resources/webview.js index f939f3b0..04b3dea0 100644 --- a/src/programflow-visualization/frontend/resources/webview.js +++ b/src/programflow-visualization/frontend/resources/webview.js @@ -63,10 +63,12 @@ function updateVisualization(traceElem) {
`; - document.getElementById("viz").innerHTML = data; - document.getElementById("viz").addEventListener("scroll", () => { + const viz = document.getElementById("viz"); + viz.innerHTML = data; + viz.addEventListener("scroll", () => { updateRefArrows(traceElem); }); + viz.scrollTo(0, viz.scrollHeight); const stdoutLog = document.getElementById("stdout-log"); stdoutLog.innerHTML = traceElem[4]; stdoutLog.scrollTo(0, stdoutLog.scrollHeight); From ba7a511d1998143bbe87c1790c9114c7655ca40e Mon Sep 17 00:00:00 2001 From: Stefan Wehr Date: Mon, 4 Nov 2024 16:52:55 +0100 Subject: [PATCH 43/43] visual improvements --- .../frontend/HTMLGenerator.ts | 42 +++++++++++++++---- .../frontend/resources/webview.css | 18 ++++---- 2 files changed, 44 insertions(+), 16 deletions(-) diff --git a/src/programflow-visualization/frontend/HTMLGenerator.ts b/src/programflow-visualization/frontend/HTMLGenerator.ts index 54231a07..28c4c1f7 100644 --- a/src/programflow-visualization/frontend/HTMLGenerator.ts +++ b/src/programflow-visualization/frontend/HTMLGenerator.ts @@ -16,6 +16,14 @@ function toID(s: any) { } } +function keyToString(key: any) { + if (key.type !== undefined) { + return key.type === 'ref' ? '' : (key.value ? key.value : key); + } else { + return key.toString(); + } +} + export class HTMLGenerator { uniqueId: number = -1; @@ -66,6 +74,7 @@ export class HTMLGenerator { private heapValue(name: string, heapValue: HeapValue): string { let result = ''; + let keyLen = 0; switch (heapValue.type) { case 'dict': const metaKeys = Array.from(Object.keys(heapValue.keys)); @@ -73,18 +82,27 @@ export class HTMLGenerator { // -> Conversion required const keyMap = new Map(Object.entries(heapValue.keys)); const valueMap = new Map(Object.entries(heapValue.value)); + keyLen = 0; + metaKeys.forEach(metaKey => { + let key = keyMap.get(metaKey)!; + keyLen = Math.max(keyToString(key).length, keyLen); + }); result = `
- ${metaKeys.map(metaKey => this.dictValue(keyMap.get(metaKey)!, valueMap.get(metaKey)!)).join('')} + ${metaKeys.map(metaKey => this.dictValue(keyLen, keyMap.get(metaKey)!, valueMap.get(metaKey)!)).join('')}
`; break; case 'instance': const instanceKeys = Array.from(Object.keys(heapValue.value)); const instanceValues = Array.from(Object.values(heapValue.value)); // maybe endpointer look for if its exist and if add a second number or key or smth + keyLen = 0; + instanceKeys.forEach(key => { + keyLen = Math.max(keyToString(key).length, keyLen); + }); result = `
- ${instanceKeys.map((key, index) => this.dictValue(key, instanceValues[index])).join('')} + ${instanceKeys.map((key, index) => this.dictValue(keyLen, key, instanceValues[index])).join('')}
`; break; @@ -113,15 +131,19 @@ export class HTMLGenerator { return result; } - private dictValue(key: any, value: Value): string { + private dictValue(maxKeyLen: number, key: any, value: Value): string { this.uniqueId++; + const keyWidth = Math.min(100, maxKeyLen * 10 + 10); + const valWidth = 200 - keyWidth; return `
-
- ${key.type === 'ref' ? '' : escapeHTML(key.value ? key.value : key)} +
+ ${escapeHTML(keyToString(key))}
-
- ${value.type === 'ref' ? '' : escapeHTML(value.value)} +
+ ${value.type === 'ref' ? '' : this.getCorrectValueOf(value)}
`; @@ -135,7 +157,7 @@ export class HTMLGenerator { ${index}
- ${value.type === 'ref' ? '' : escapeHTML(value.value)} + ${value.type === 'ref' ? '' : this.getCorrectValueOf(value)}
`; @@ -146,7 +168,7 @@ export class HTMLGenerator { return `
- ${value.type === 'ref' ? '' : escapeHTML(value.value)} + ${value.type === 'ref' ? '' : this.getCorrectValueOf(value)}
`; @@ -183,6 +205,8 @@ export class HTMLGenerator { switch (value.type) { case 'ref': return ''; + case 'none': + return 'None'; default: return escapeHTML(`${value.value}`); } diff --git a/src/programflow-visualization/frontend/resources/webview.css b/src/programflow-visualization/frontend/resources/webview.css index c374f365..4f8046f5 100644 --- a/src/programflow-visualization/frontend/resources/webview.css +++ b/src/programflow-visualization/frontend/resources/webview.css @@ -148,28 +148,32 @@ body { .box { color: white; - border: 1px solid rgba(109, 89, 89, 1.0); - background-color: rgba(69, 69, 69, 0.5); + border: 1px solid rgb(209, 209, 209); + background-color: rgba(56, 56, 56, 0.8); } .box-content-top { margin: 2px 2px 2px 5px; - color: grey; + color: rgb(184, 184, 184); } .box-content-bottom { - margin: 10px 15px 10px 15px; + margin: 2px 5px 2px 5px; } .box-content-dict { - padding: 10px 15px 10px 15px; - max-width: 100px; - min-width: 100px; + padding: 2px 5px 2px 5px; + /* max-width: 100px; + min-width: 100px; */ text-overflow: ellipsis; overflow: hidden; white-space: nowrap; } +.box-content-dict-key { + text-align: right; +} + .box-set { flex-wrap: wrap; width: 150px;