diff --git a/CHANGELOG.md b/CHANGELOG.md index 5f4e3b9b..16d7dc17 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -2,6 +2,23 @@ All notable changes to the **kdb VS Code extension** are documented in this file. +# v1.8.0 + +### Enhancements + +- Added the ability to add multiple labels to a single connection +- Show KDB+ process explorer item content when clicked +- Added the ability to export and import connections +- All the files in the workspace are considered when using language server features +- Show call hierarchy is implemented in language server +- Query history shows an ellipsis of the query execution text to the available line length +- Added limit option to datasource for 1.11 + versions of Insights Enterprise connections + +### Fixes + +- Fixed KDB results columns resizing back to default sizes every time a datasource was run +- Fixed KDB results for large data sets + # v1.7.0 ### Enhancements diff --git a/README.md b/README.md index 59fe3b5c..34bc95dc 100644 --- a/README.md +++ b/README.md @@ -282,6 +282,34 @@ When editing a **Insights** connection, you can edit the following properties: ![Edit Insights connection](https://github.com/KxSystems/kx-vscode/blob/main/img/edit-insights-conn-form.png?raw=true) +### Import/Export Connection Configuration + +The **Import/Export Connection** config allows you to import and export connections in JSON format from the VSCode IDE without having to create them manually. + +![Import Export](https://github.com/KxSystems/kx-vscode/blob/main/img/impex.png?raw=true) + +To import a connection: + +1. Open the Command Palette (Ctrl+Shift+P) and type the command to open the connection configuration for the installed database extension **OR** click the three dots (…) next to the Refresh button in the Connections window. +2. Select **Import Connections**. +3. Navigate to the location of the configuration file, such as a JSON file that contains the connection details, and select it. +4. Review the imported connection for accuracy. +5. Confirm the import. + +To connect to the database, select the newly imported connection from the list of available connections and initiate the connection to the database. You can run a simple query or command to verify the connection is successful. + +Note: If the imported connection has the same name as an existing connection, you will see a notification in the bottom right corner prompting you to either duplicate, overwrite, or cancel the import. + +To export a connection: + +1. Open the Command Palette (Ctrl+Shift+P) and type the command to manage connection configurations for the installed database extension **OR** click the three dots (…) next to the Refresh button in the Connections window. +2. Select **Export Connections**. +3. Choose the connection(s) you want to export. +4. Specify the format and location for the exported configuration file. For example, JSON, YAML. +5. Confirm the export action. + +To verify the export is successful navigate to the saved location and open the configuration file to check its contents. + ## Connection Labels Connection Labels allow you to categorize and organize your connections by assigning them distinct names and colors, making it easier to manage and locate specific connections within the application. @@ -427,13 +455,14 @@ To create a data source and run it against a specific connection: 1. Ensure you have at least one folder open in VS Code. 1. In the **DATASOURCES** view, click **+** and specify the parameters defined in the following table: - | Property | Description | - | ----------------------- | ---------------------------------------------------------------- | - | **Connection** | Select a Connection from the **Connection** dropdown. | - | **Select API** | Choose **getData** from the **Select API** dropdown. | - | **Table** | Choose the table you wish to query from the **Tables** dropdown. | - | **Start Time/End Time** | Select the **Start Time** and **End Time** for the query. | - | Additional Parameters | You can choose from the additional parameters as required. | + | Property | Description | + | ----------------------- | ------------------------------------------------------------------------------------------------------------------- | + | **Connection** | Select a Connection from the **Connection** dropdown. | + | **Select API** | Choose **getData** from the **Select API** dropdown. | + | **Table** | Choose the table you wish to query from the **Tables** dropdown. | + | **Start Time/End Time** | Select the **Start Time** and **End Time** for the query. | + | **Row Limit** | Add a limit to the number of queries executed to reduce the number of Out of Memory (OOM) issues or failed queries. | + | Additional Parameters | You can choose from the additional parameters as required. | 1. Click **Save** to store the settings you have chosen, for reuse later. When you save a data source; query parameters and the connection details are stored. The data source icon is green if it is associated with a connection and grey if there is no association. @@ -586,9 +615,20 @@ To update kdb VS Code settings, search for **kdb** from _Preferences_ > _Setting | **Hide subscription to newsletter after first install** | yes/no; default no | | **Insights Enterprise Connections for Explorer** | [edit JSON settings](#insights-enterprise-connections-for-explorer) | | **Linting** | Enable linting for q and quke files | +| **Refactoring** | Choose refactoring scope | | **QHOME directory for q runtime** | Display location path of q installation | | **Servers** | [edit JSON settings](#servers) | +### Refactoring + +By default, refactorings like renaming are applied to all files in the workspace. You can preview the changes before applying them and select specific files to apply the refactoring by pressing the **ctrl** or **command** key before executing the action. + +![Preview](https://github.com/KxSystems/kx-vscode/blob/main/img/preview.png?raw=true) + +If you only need to apply the refactorings to the currently opened files, you can select **Window** instead of **Workspace** for the refactoring option: + +![Refactoring](https://github.com/KxSystems/kx-vscode/blob/main/img/refactoring.png?raw=true) + ### kdb Insights Enterprise Connections for Explorer ```JSON diff --git a/img/impex.png b/img/impex.png new file mode 100644 index 00000000..9161b300 Binary files /dev/null and b/img/impex.png differ diff --git a/img/outline.png b/img/outline.png index dab2e7de..cb08041e 100644 Binary files a/img/outline.png and b/img/outline.png differ diff --git a/img/preview.png b/img/preview.png new file mode 100644 index 00000000..0fa978f1 Binary files /dev/null and b/img/preview.png differ diff --git a/img/refactoring.png b/img/refactoring.png new file mode 100644 index 00000000..5ee0de69 Binary files /dev/null and b/img/refactoring.png differ diff --git a/package-lock.json b/package-lock.json index cefd8212..875901c3 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,24 +1,21 @@ { "name": "kdb", - "version": "1.7.0", + "version": "1.8.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "kdb", - "version": "1.7.0", + "version": "1.8.0", "license": "MIT", "dependencies": { - "@types/graceful-fs": "^4.1.9", "@vscode/webview-ui-toolkit": "^1.4.0", "@windozer/node-q": "^2.6.0", - "ag-grid-community": "^32.0.1", - "axios": "^1.7.4", + "ag-grid-community": "^32.1.0", + "axios": "^1.7.7", "chevrotain": "^10.5.0", - "csv-parser": "^3.0.0", "extract-zip": "^2.0.1", "fs-extra": "^11.2.0", - "fuse.js": "^7.0.0", "jwt-decode": "^4.0.0", "moment": "^2.30.1", "moment-duration-format": "^2.3.2", @@ -46,27 +43,27 @@ "@types/sinon": "^17.0.3", "@types/vscode": "^1.86.0", "@types/vscode-webview": "^1.57.5", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", "@vscode/test-electron": "^2.3.10", - "esbuild": "^0.19.12", - "eslint": "^8.57.0", + "esbuild": "^0.23.1", + "eslint": "^9.10.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-license-header": "^0.6.1", - "eslint-plugin-unused-imports": "^3.2.0", + "eslint-plugin-unused-imports": "^4.1.3", "glob": "^8.1.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-instrument": "^3.3.0", "istanbul-lib-report": "^2.0.8", "istanbul-lib-source-maps": "^3.0.6", "istanbul-reports": "^3.1.7", - "lit": "^3.1.3", + "lit": "^3.2.0", "mocha": "^10.4.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "mock-fs": "^5.2.0", - "prettier": "^3.2.5", - "rimraf": "^5.0.7", + "prettier": "^3.3.3", + "rimraf": "^6.0.1", "sinon": "^17.0.1", "typescript": "^5.4.5", "vscode-dts": "^0.3.3", @@ -357,371 +354,411 @@ "integrity": "sha512-hBzuU5+JjB2cqNZyszkDHZgOSrUUT8V3dhgRl8Q9Gp6dAj/H5+KILGjbhDpc3Iy9qmqlm/akuOI2ut9VUtzJxQ==" }, "node_modules/@esbuild/aix-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.19.12.tgz", - "integrity": "sha512-bmoCYyWdEL3wDQIVbcyzRyeKLgk2WtWLTWz1ZIAZF/EGbNOwSA6ew3PftJ1PqMiOOGu0OyFMzG53L0zqIpPeNA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/aix-ppc64/-/aix-ppc64-0.23.1.tgz", + "integrity": "sha512-6VhYk1diRqrhBAqpJEdjASR/+WVRtfjpqKuNw11cLiaWpAT/Uu+nokB+UJnevzy/P9C/ty6AOe0dwueMrGh/iQ==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "aix" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.19.12.tgz", - "integrity": "sha512-qg/Lj1mu3CdQlDEEiWrlC4eaPZ1KztwGJ9B6J+/6G+/4ewxJg7gqj8eVYWvao1bXrqGiW2rsBZFSX3q2lcW05w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm/-/android-arm-0.23.1.tgz", + "integrity": "sha512-uz6/tEy2IFm9RYOyvKl88zdzZfwEfKZmnX9Cj1BHjeSGNuGLuMD1kR8y5bteYmwqKm1tj8m4cb/aKEorr6fHWQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.19.12.tgz", - "integrity": "sha512-P0UVNGIienjZv3f5zq0DP3Nt2IE/3plFzuaS96vihvD0Hd6H/q4WXUGpCxD/E8YrSXfNyRPbpTq+T8ZQioSuPA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-arm64/-/android-arm64-0.23.1.tgz", + "integrity": "sha512-xw50ipykXcLstLeWH7WRdQuysJqejuAGPd30vd1i5zSyKK3WE+ijzHmLKxdiCMtH1pHz78rOg0BKSYOSB/2Khw==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/android-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.19.12.tgz", - "integrity": "sha512-3k7ZoUW6Q6YqhdhIaq/WZ7HwBpnFBlW905Fa4s4qWJyiNOgT1dOqDiVAQFwBH7gBRZr17gLrlFCRzF6jFh7Kew==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/android-x64/-/android-x64-0.23.1.tgz", + "integrity": "sha512-nlN9B69St9BwUoB+jkyU090bru8L0NA3yFvAd7k8dNsVH8bi9a8cUAUSEcEEgTp2z3dbEDGJGfP6VUnkQnlReg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "android" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.19.12.tgz", - "integrity": "sha512-B6IeSgZgtEzGC42jsI+YYu9Z3HKRxp8ZT3cqhvliEHovq8HSX2YX8lNocDn79gCKJXOSaEot9MVYky7AKjCs8g==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-arm64/-/darwin-arm64-0.23.1.tgz", + "integrity": "sha512-YsS2e3Wtgnw7Wq53XXBLcV6JhRsEq8hkfg91ESVadIrzr9wO6jJDMZnCQbHm1Guc5t/CdDiFSSfWP58FNuvT3Q==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/darwin-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.19.12.tgz", - "integrity": "sha512-hKoVkKzFiToTgn+41qGhsUJXFlIjxI/jSYeZf3ugemDYZldIXIxhvwN6erJGlX4t5h417iFuheZ7l+YVn05N3A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/darwin-x64/-/darwin-x64-0.23.1.tgz", + "integrity": "sha512-aClqdgTDVPSEGgoCS8QDG37Gu8yc9lTHNAQlsztQ6ENetKEO//b8y31MMu2ZaPbn4kVsIABzVLXYLhCGekGDqw==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "darwin" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.19.12.tgz", - "integrity": "sha512-4aRvFIXmwAcDBw9AueDQ2YnGmz5L6obe5kmPT8Vd+/+x/JMVKCgdcRwH6APrbpNXsPz+K653Qg8HB/oXvXVukA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-arm64/-/freebsd-arm64-0.23.1.tgz", + "integrity": "sha512-h1k6yS8/pN/NHlMl5+v4XPfikhJulk4G+tKGFIOwURBSFzE8bixw1ebjluLOjfwtLqY0kewfjLSrO6tN2MgIhA==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/freebsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.19.12.tgz", - "integrity": "sha512-EYoXZ4d8xtBoVN7CEwWY2IN4ho76xjYXqSXMNccFSx2lgqOG/1TBPW0yPx1bJZk94qu3tX0fycJeeQsKovA8gg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/freebsd-x64/-/freebsd-x64-0.23.1.tgz", + "integrity": "sha512-lK1eJeyk1ZX8UklqFd/3A60UuZ/6UVfGT2LuGo3Wp4/z7eRTRYY+0xOu2kpClP+vMTi9wKOfXi2vjUpO1Ro76g==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "freebsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.19.12.tgz", - "integrity": "sha512-J5jPms//KhSNv+LO1S1TX1UWp1ucM6N6XuL6ITdKWElCu8wXP72l9MM0zDTzzeikVyqFE6U8YAV9/tFyj0ti+w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm/-/linux-arm-0.23.1.tgz", + "integrity": "sha512-CXXkzgn+dXAPs3WBwE+Kvnrf4WECwBdfjfeYHpMeVxWE0EceB6vhWGShs6wi0IYEqMSIzdOF1XjQ/Mkm5d7ZdQ==", "cpu": [ "arm" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.19.12.tgz", - "integrity": "sha512-EoTjyYyLuVPfdPLsGVVVC8a0p1BFFvtpQDB/YLEhaXyf/5bczaGeN15QkR+O4S5LeJ92Tqotve7i1jn35qwvdA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-arm64/-/linux-arm64-0.23.1.tgz", + "integrity": "sha512-/93bf2yxencYDnItMYV/v116zff6UyTjo4EtEQjUBeGiVpMmffDNUyD9UN2zV+V3LRV3/on4xdZ26NKzn6754g==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.19.12.tgz", - "integrity": "sha512-Thsa42rrP1+UIGaWz47uydHSBOgTUnwBwNq59khgIwktK6x60Hivfbux9iNR0eHCHzOLjLMLfUMLCypBkZXMHA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ia32/-/linux-ia32-0.23.1.tgz", + "integrity": "sha512-VTN4EuOHwXEkXzX5nTvVY4s7E/Krz7COC8xkftbbKRYAl96vPiUssGkeMELQMOnLOJ8k3BY1+ZY52tttZnHcXQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-loong64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.19.12.tgz", - "integrity": "sha512-LiXdXA0s3IqRRjm6rV6XaWATScKAXjI4R4LoDlvO7+yQqFdlr1Bax62sRwkVvRIrwXxvtYEHHI4dm50jAXkuAA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-loong64/-/linux-loong64-0.23.1.tgz", + "integrity": "sha512-Vx09LzEoBa5zDnieH8LSMRToj7ir/Jeq0Gu6qJ/1GcBq9GkfoEAoXvLiW1U9J1qE/Y/Oyaq33w5p2ZWrNNHNEw==", "cpu": [ "loong64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-mips64el": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.19.12.tgz", - "integrity": "sha512-fEnAuj5VGTanfJ07ff0gOA6IPsvrVHLVb6Lyd1g2/ed67oU1eFzL0r9WL7ZzscD+/N6i3dWumGE1Un4f7Amf+w==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-mips64el/-/linux-mips64el-0.23.1.tgz", + "integrity": "sha512-nrFzzMQ7W4WRLNUOU5dlWAqa6yVeI0P78WKGUo7lg2HShq/yx+UYkeNSE0SSfSure0SqgnsxPvmAUu/vu0E+3Q==", "cpu": [ "mips64el" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-ppc64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.19.12.tgz", - "integrity": "sha512-nYJA2/QPimDQOh1rKWedNOe3Gfc8PabU7HT3iXWtNUbRzXS9+vgB0Fjaqr//XNbd82mCxHzik2qotuI89cfixg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-ppc64/-/linux-ppc64-0.23.1.tgz", + "integrity": "sha512-dKN8fgVqd0vUIjxuJI6P/9SSSe/mB9rvA98CSH2sJnlZ/OCZWO1DJvxj8jvKTfYUdGfcq2dDxoKaC6bHuTlgcw==", "cpu": [ "ppc64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-riscv64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.19.12.tgz", - "integrity": "sha512-2MueBrlPQCw5dVJJpQdUYgeqIzDQgw3QtiAHUC4RBz9FXPrskyyU3VI1hw7C0BSKB9OduwSJ79FTCqtGMWqJHg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-riscv64/-/linux-riscv64-0.23.1.tgz", + "integrity": "sha512-5AV4Pzp80fhHL83JM6LoA6pTQVWgB1HovMBsLQ9OZWLDqVY8MVobBXNSmAJi//Csh6tcY7e7Lny2Hg1tElMjIA==", "cpu": [ "riscv64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-s390x": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.19.12.tgz", - "integrity": "sha512-+Pil1Nv3Umes4m3AZKqA2anfhJiVmNCYkPchwFJNEJN5QxmTs1uzyy4TvmDrCRNT2ApwSari7ZIgrPeUx4UZDg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-s390x/-/linux-s390x-0.23.1.tgz", + "integrity": "sha512-9ygs73tuFCe6f6m/Tb+9LtYxWR4c9yg7zjt2cYkjDbDpV/xVn+68cQxMXCjUpYwEkze2RcU/rMnfIXNRFmSoDw==", "cpu": [ "s390x" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/linux-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.19.12.tgz", - "integrity": "sha512-B71g1QpxfwBvNrfyJdVDexenDIt1CiDN1TIXLbhOw0KhJzE78KIFGX6OJ9MrtC0oOqMWf+0xop4qEU8JrJTwCg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/linux-x64/-/linux-x64-0.23.1.tgz", + "integrity": "sha512-EV6+ovTsEXCPAp58g2dD68LxoP/wK5pRvgy0J/HxPGB009omFPv3Yet0HiaqvrIrgPTBuC6wCH1LTOY91EO5hQ==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "linux" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/netbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.19.12.tgz", - "integrity": "sha512-3ltjQ7n1owJgFbuC61Oj++XhtzmymoCihNFgT84UAmJnxJfm4sYCiSLTXZtE00VWYpPMYc+ZQmB6xbSdVh0JWA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/netbsd-x64/-/netbsd-x64-0.23.1.tgz", + "integrity": "sha512-aevEkCNu7KlPRpYLjwmdcuNz6bDFiE7Z8XC4CPqExjTvrHugh28QzUXVOZtiYghciKUacNktqxdpymplil1beA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "netbsd" ], "engines": { - "node": ">=12" + "node": ">=18" + } + }, + "node_modules/@esbuild/openbsd-arm64": { + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-arm64/-/openbsd-arm64-0.23.1.tgz", + "integrity": "sha512-3x37szhLexNA4bXhLrCC/LImN/YtWis6WXr1VESlfVtVeoFJBRINPJ3f0a/6LV8zpikqoUg4hyXw0sFBt5Cr+Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "license": "MIT", + "optional": true, + "os": [ + "openbsd" + ], + "engines": { + "node": ">=18" } }, "node_modules/@esbuild/openbsd-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.19.12.tgz", - "integrity": "sha512-RbrfTB9SWsr0kWmb9srfF+L933uMDdu9BIzdA7os2t0TXhCRjrQyCeOt6wVxr79CKD4c+p+YhCj31HBkYcXebw==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/openbsd-x64/-/openbsd-x64-0.23.1.tgz", + "integrity": "sha512-aY2gMmKmPhxfU+0EdnN+XNtGbjfQgwZj43k8G3fyrDM/UdZww6xrWxmDkuz2eCZchqVeABjV5BpildOrUbBTqA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "openbsd" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/sunos-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.19.12.tgz", - "integrity": "sha512-HKjJwRrW8uWtCQnQOz9qcU3mUZhTUQvi56Q8DPTLLB+DawoiQdjsYq+j+D3s9I8VFtDr+F9CjgXKKC4ss89IeA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/sunos-x64/-/sunos-x64-0.23.1.tgz", + "integrity": "sha512-RBRT2gqEl0IKQABT4XTj78tpk9v7ehp+mazn2HbUeZl1YMdaGAQqhapjGTCe7uw7y0frDi4gS0uHzhvpFuI1sA==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "sunos" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-arm64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.19.12.tgz", - "integrity": "sha512-URgtR1dJnmGvX864pn1B2YUYNzjmXkuJOIqG2HdU62MVS4EHpU2946OZoTMnRUHklGtJdJZ33QfzdjGACXhn1A==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-arm64/-/win32-arm64-0.23.1.tgz", + "integrity": "sha512-4O+gPR5rEBe2FpKOVyiJ7wNDPA8nGzDuJ6gN4okSA1gEOYZ67N8JPk58tkWtdtPeLz7lBnY6I5L3jdsr3S+A6A==", "cpu": [ "arm64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-ia32": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.19.12.tgz", - "integrity": "sha512-+ZOE6pUkMOJfmxmBZElNOx72NKpIa/HFOMGzu8fqzQJ5kgf6aTGrcJaFsNiVMH4JKpMipyK+7k0n2UXN7a8YKQ==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-ia32/-/win32-ia32-0.23.1.tgz", + "integrity": "sha512-BcaL0Vn6QwCwre3Y717nVHZbAa4UBEigzFm6VdsVdT/MbZ38xoj1X9HPkZhbmaBGUD1W8vxAfffbDe8bA6AKnQ==", "cpu": [ "ia32" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@esbuild/win32-x64": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.19.12.tgz", - "integrity": "sha512-T1QyPSDCyMXaO3pzBkF96E8xMkiRYbUEZADd29SyPGabqxMViNoii+NcK7eWJAEoU6RZyEm5lVSIjTmcdoB9HA==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/@esbuild/win32-x64/-/win32-x64-0.23.1.tgz", + "integrity": "sha512-BHpFFeslkWrXWyUPnbKm+xYYVYruCinGcftSBaa8zoF9hZO4BcSCFUvHVTtzpIY6YzUnYtuEhZ+C9iEXjxnasg==", "cpu": [ "x64" ], "dev": true, + "license": "MIT", "optional": true, "os": [ "win32" ], "engines": { - "node": ">=12" + "node": ">=18" } }, "node_modules/@eslint-community/eslint-utils": { @@ -740,24 +777,65 @@ } }, "node_modules/@eslint-community/regexpp": { - "version": "4.10.0", - "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.10.0.tgz", - "integrity": "sha512-Cu96Sd2By9mCNTx2iyKOmq10v22jUVQv0lQnlGNy16oE9589yE+QADPbrMGCkA51cKZSg3Pu/aTJVTGfL/qjUA==", + "version": "4.11.0", + "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.11.0.tgz", + "integrity": "sha512-G/M/tIiMrTAxEWRfLfQJMmGNX28IxBg4PBz8XqQhqUHLFI6TL2htpIB1iQCj144V5ee/JaKyT9/WZ0MGZWfA7A==", "dev": true, + "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, + "node_modules/@eslint/config-array": { + "version": "0.18.0", + "resolved": "https://registry.npmjs.org/@eslint/config-array/-/config-array-0.18.0.tgz", + "integrity": "sha512-fTxvnS1sRMu3+JjXwJG0j/i4RT9u4qJ+lqS/yCGap4lH4zZGzQ7tu+xZqQmcMZq5OBZDL4QRxQzRjkWcGt8IVw==", + "dev": true, + "license": "Apache-2.0", + "dependencies": { + "@eslint/object-schema": "^2.1.4", + "debug": "^4.3.1", + "minimatch": "^3.1.2" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + } + }, + "node_modules/@eslint/config-array/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, + "license": "MIT", + "dependencies": { + "balanced-match": "^1.0.0", + "concat-map": "0.0.1" + } + }, + "node_modules/@eslint/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/@eslint/eslintrc": { - "version": "2.1.4", - "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", - "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-3.1.0.tgz", + "integrity": "sha512-4Bfj15dVJdoy3RfZmmo86RK1Fwzn6SstsvK9JS+BaVKqC6QQQQyXekNaC+g+LKNgkQ+2VhGAzm6hO40AhMR3zQ==", "dev": true, + "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", - "espree": "^9.6.0", - "globals": "^13.19.0", + "espree": "^10.0.1", + "globals": "^14.0.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", @@ -765,7 +843,7 @@ "strip-json-comments": "^3.1.1" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -776,6 +854,7 @@ "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", "dev": true, + "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -786,6 +865,7 @@ "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" }, @@ -794,48 +874,36 @@ } }, "node_modules/@eslint/js": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.0.tgz", - "integrity": "sha512-Ys+3g2TaW7gADOJzPt83SJtCDhMjndcDMFVQ/Tj9iA1BfJzFKD9mAUXT3OenpuPHbI6P/myECxRJrofUsDx/5g==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/@eslint/js/-/js-9.10.0.tgz", + "integrity": "sha512-fuXtbiP5GWIn8Fz+LWoOMVf/Jxm+aajZYkhi6CuEm4SxymFM+eUWzbO9qXT+L0iCkL5+KGYMCSGxo686H19S1g==", "dev": true, + "license": "MIT", "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array": { - "version": "0.11.14", - "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.11.14.tgz", - "integrity": "sha512-3T8LkOmg45BV5FICb15QQMsyUSWrQ8AygVfC7ZG32zOalnqrilm018ZVCw0eapXux8FtA33q8PSRSstjee3jSg==", + "node_modules/@eslint/object-schema": { + "version": "2.1.4", + "resolved": "https://registry.npmjs.org/@eslint/object-schema/-/object-schema-2.1.4.tgz", + "integrity": "sha512-BsWiH1yFGjXXS2yvrf5LyuoSIIbPrGUWob917o+BTKuZ7qJdxX8aJLRxs1fS9n6r7vESrq1OUqb68dANcFXuQQ==", "dev": true, - "dependencies": { - "@humanwhocodes/object-schema": "^2.0.2", - "debug": "^4.3.1", - "minimatch": "^3.0.5" - }, + "license": "Apache-2.0", "engines": { - "node": ">=10.10.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, - "node_modules/@humanwhocodes/config-array/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "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==", + "node_modules/@eslint/plugin-kit": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/@eslint/plugin-kit/-/plugin-kit-0.1.0.tgz", + "integrity": "sha512-autAXT203ixhqei9xt+qkYOvY8l6LAFIdT2UXc/RPNeUVfqRF1BV94GTJyVPFKT8nFM6MyVJhjLj9E8JWvf5zQ==", "dev": true, + "license": "Apache-2.0", "dependencies": { - "brace-expansion": "^1.1.7" + "levn": "^0.4.1" }, "engines": { - "node": "*" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" } }, "node_modules/@humanwhocodes/module-importer": { @@ -851,17 +919,26 @@ "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==", - "dev": true + "node_modules/@humanwhocodes/retry": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/@humanwhocodes/retry/-/retry-0.3.0.tgz", + "integrity": "sha512-d2CGZR2o7fS6sWB7DG/3a95bGKQyHMACZ5aW8qGkkqQpUoZV6C0X7Pc7l4ZNMZkfNBf4VWNe9E1jRsf0G146Ew==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": ">=18.18" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/nzakas" + } }, "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, + "license": "ISC", "dependencies": { "string-width": "^5.1.2", "string-width-cjs": "npm:string-width@^4.2.0", @@ -879,6 +956,7 @@ "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.0.1.tgz", "integrity": "sha512-n5M855fKb2SsfMIiFFoVrABHJC8QtHwVx+mHWP3QcEqBHYienj5dHSgjbxtC0WEZXYt4wcD6zrQElDPhFuZgfA==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -891,6 +969,7 @@ "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-6.2.1.tgz", "integrity": "sha512-bN798gFfQX+viw3R7yrGWRqnrN2oRkEkUjjl4JNn4E8GxxbjtG3FbrEIIY3l8/hrwUwIeCZvi4QuOTP4MErVug==", "dev": true, + "license": "MIT", "engines": { "node": ">=12" }, @@ -902,13 +981,15 @@ "version": "9.2.2", "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-9.2.2.tgz", "integrity": "sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==", - "dev": true + "dev": true, + "license": "MIT" }, "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, + "license": "MIT", "dependencies": { "eastasianwidth": "^0.2.0", "emoji-regex": "^9.2.2", @@ -926,6 +1007,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.1.0.tgz", "integrity": "sha512-iq6eVVI64nQQTRYq2KtEg2d2uU7LElhTJwsH4YzIHZshxlgZms/wIc4VoDQTlG/IvVIrBKG06CrZnp0qv7hkcQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^6.0.1" }, @@ -941,6 +1023,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-8.1.0.tgz", "integrity": "sha512-si7QWI6zUMq56bESFvagtmzMdGOtoxfR+Sez11Mobfc7tm+VkUckk9bW2UeffTGVUbOksxmSw0AA2gs8g71NCQ==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^6.1.0", "string-width": "^5.0.1", @@ -1002,16 +1085,18 @@ } }, "node_modules/@lit-labs/ssr-dom-shim": { - "version": "1.2.0", - "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.0.tgz", - "integrity": "sha512-yWJKmpGE6lUURKAaIltoPIE/wrbY3TEkqQt+X0m+7fQNnAv0keydnYvbiJFP1PnMhizmIWRWOG5KLhYyc/xl+g==", - "dev": true + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.2.1.tgz", + "integrity": "sha512-wx4aBmgeGvFmOKucFKY+8VFJSYZxs9poN3SDNQFF6lT6NrQUnHiPB2PWz2sc4ieEcAaYYzN+1uWahEeTq2aRIQ==", + "dev": true, + "license": "BSD-3-Clause" }, "node_modules/@lit/reactive-element": { "version": "2.0.4", "resolved": "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-2.0.4.tgz", "integrity": "sha512-GFn91inaUa2oHLak8awSIigYz0cU0Payr1rcFsrkf5OJ5eSPxElyZfKh0f2p9FsTiZWXQdWGJeXZICEfXXYSXQ==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0" } @@ -1097,6 +1182,7 @@ "resolved": "https://registry.npmjs.org/@pkgjs/parseargs/-/parseargs-0.11.0.tgz", "integrity": "sha512-+1VkjdD0QBLPodGrJUeqarH8VAIvQODIbwh9XpP5Syisf7YoQgsJKPNFoqqLQlu+VQ/tVSshMR6loPMn8U+dPg==", "dev": true, + "license": "MIT", "optional": true, "engines": { "node": ">=14" @@ -1293,7 +1379,8 @@ "version": "2.0.7", "resolved": "https://registry.npmjs.org/@types/trusted-types/-/trusted-types-2.0.7.tgz", "integrity": "sha512-ScaPdn1dQczgbl0QFTeTOmVHFULt394XJgOQNoyVhZ6r2vLnMLJfBPd53SB52T/3G36VI1/g2MZaX0cwDuXsfw==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/@types/vscode": { "version": "1.89.0", @@ -1317,31 +1404,32 @@ } }, "node_modules/@typescript-eslint/eslint-plugin": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-7.9.0.tgz", - "integrity": "sha512-6e+X0X3sFe/G/54aC3jt0txuMTURqLyekmEHViqyA2VnxhLMpvA6nqmcjIy+Cr9tLDHPssA74BP5Mx9HQIxBEA==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-8.4.0.tgz", + "integrity": "sha512-rg8LGdv7ri3oAlenMACk9e+AR4wUV0yrrG+XKsGKOK0EVgeEDqurkXMPILG2836fW4ibokTB5v4b6Z9+GYQDEw==", "dev": true, + "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.10.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/type-utils": "7.9.0", - "@typescript-eslint/utils": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/type-utils": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "graphemer": "^1.4.0", "ignore": "^5.3.1", "natural-compare": "^1.4.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "@typescript-eslint/parser": "^7.0.0", - "eslint": "^8.56.0" + "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1349,26 +1437,28 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/type-utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-7.9.0.tgz", - "integrity": "sha512-6Qy8dfut0PFrFRAZsGzuLoM4hre4gjzWJB6sUvdunCYZsYemTkzZNwF1rnGea326PHPT3zn5Lmg32M/xfJfByA==", + "node_modules/@typescript-eslint/parser": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-8.4.0.tgz", + "integrity": "sha512-NHgWmKSgJk5K9N16GIhQ4jSobBoJwrmURaLErad0qlLjrpP5bECYg+wxVTGlGZmJbU03jj/dfnb6V9bw+5icsA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/utils": "7.9.0", - "debug": "^4.3.4", - "ts-api-utils": "^1.3.0" + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", + "debug": "^4.3.4" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { - "eslint": "^8.56.0" + "eslint": "^8.57.0 || ^9.0.0" }, "peerDependenciesMeta": { "typescript": { @@ -1376,80 +1466,57 @@ } } }, - "node_modules/@typescript-eslint/eslint-plugin/node_modules/@typescript-eslint/utils": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-7.9.0.tgz", - "integrity": "sha512-5KVRQCzZajmT4Ep+NEgjXCvjuypVvYHUW7RHlXzNPuak2oWpVoD1jf5xCP0dPAuNIchjC7uQyvbdaSTFaLqSdA==", + "node_modules/@typescript-eslint/scope-manager": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-8.4.0.tgz", + "integrity": "sha512-n2jFxLeY0JmKfUqy3P70rs6vdoPjHK8P/w+zJcV3fk0b0BwRXC/zxRTEnAsgYT7MwdQDt/ZEbtdzdVC+hcpF0A==", "dev": true, + "license": "MIT", "dependencies": { - "@eslint-community/eslint-utils": "^4.4.0", - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0" + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" - }, - "peerDependencies": { - "eslint": "^8.56.0" } }, - "node_modules/@typescript-eslint/parser": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-7.9.0.tgz", - "integrity": "sha512-qHMJfkL5qvgQB2aLvhUSXxbK7OLnDkwPzFalg458pxQgfxKDfT1ZDbHQM/I6mDIf/svlMkj21kzKuQ2ixJlatQ==", + "node_modules/@typescript-eslint/type-utils": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-8.4.0.tgz", + "integrity": "sha512-pu2PAmNrl9KX6TtirVOrbLPLwDmASpZhK/XU7WvoKoCUkdtq9zF7qQ7gna0GBZFN0hci0vHaSusiL2WpsQk37A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/scope-manager": "7.9.0", - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/typescript-estree": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", - "debug": "^4.3.4" + "@typescript-eslint/typescript-estree": "8.4.0", + "@typescript-eslint/utils": "8.4.0", + "debug": "^4.3.4", + "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, - "peerDependencies": { - "eslint": "^8.56.0" - }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, - "node_modules/@typescript-eslint/scope-manager": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-7.9.0.tgz", - "integrity": "sha512-ZwPK4DeCDxr3GJltRz5iZejPFAAr4Wk3+2WIBaj1L5PYK5RgxExu/Y68FFVclN0y6GGwH8q+KgKRCvaTmFBbgQ==", - "dev": true, - "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0" - }, - "engines": { - "node": "^18.18.0 || >=20.0.0" - }, - "funding": { - "type": "opencollective", - "url": "https://opencollective.com/typescript-eslint" - } - }, "node_modules/@typescript-eslint/types": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-7.9.0.tgz", - "integrity": "sha512-oZQD9HEWQanl9UfsbGVcZ2cGaR0YT5476xfWE0oE5kQa2sNK2frxOlkeacLOTh9po4AlUT5rtkGyYM5kew0z5w==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-8.4.0.tgz", + "integrity": "sha512-T1RB3KQdskh9t3v/qv7niK6P8yvn7ja1mS7QK7XfRVL6wtZ8/mFs/FHf4fKvTA0rKnqnYxl/uHFNbnEt0phgbw==", "dev": true, + "license": "MIT", "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1457,22 +1524,23 @@ } }, "node_modules/@typescript-eslint/typescript-estree": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-7.9.0.tgz", - "integrity": "sha512-zBCMCkrb2YjpKV3LA0ZJubtKCDxLttxfdGmwZvTqqWevUPN0FZvSI26FalGFFUZU/9YQK/A4xcQF9o/VVaCKAg==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-8.4.0.tgz", + "integrity": "sha512-kJ2OIP4dQw5gdI4uXsaxUZHRwWAGpREJ9Zq6D5L0BweyOrWsL6Sz0YcAZGWhvKnH7fm1J5YFE1JrQL0c9dd53A==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "@typescript-eslint/types": "7.9.0", - "@typescript-eslint/visitor-keys": "7.9.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/visitor-keys": "8.4.0", "debug": "^4.3.4", - "globby": "^11.1.0", + "fast-glob": "^3.3.2", "is-glob": "^4.0.3", "minimatch": "^9.0.4", "semver": "^7.6.0", "ts-api-utils": "^1.3.0" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", @@ -1484,29 +1552,47 @@ } } }, + "node_modules/@typescript-eslint/utils": { + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-8.4.0.tgz", + "integrity": "sha512-swULW8n1IKLjRAgciCkTCafyTHHfwVQFt8DovmaF69sKbOxTSFMmIZaSHjqO9i/RV0wIblaawhzvtva8Nmm7lQ==", + "dev": true, + "license": "MIT", + "dependencies": { + "@eslint-community/eslint-utils": "^4.4.0", + "@typescript-eslint/scope-manager": "8.4.0", + "@typescript-eslint/types": "8.4.0", + "@typescript-eslint/typescript-estree": "8.4.0" + }, + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/typescript-eslint" + }, + "peerDependencies": { + "eslint": "^8.57.0 || ^9.0.0" + } + }, "node_modules/@typescript-eslint/visitor-keys": { - "version": "7.9.0", - "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-7.9.0.tgz", - "integrity": "sha512-iESPx2TNLDNGQLyjKhUvIKprlP49XNEK+MvIf9nIO7ZZaZdbnfWKHnXAgufpxqfA0YryH8XToi4+CjBgVnFTSQ==", + "version": "8.4.0", + "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-8.4.0.tgz", + "integrity": "sha512-zTQD6WLNTre1hj5wp09nBIDiOc2U5r/qmzo7wxPn4ZgAjHql09EofqhF9WF+fZHzL5aCyaIpPcT2hyxl73kr9A==", "dev": true, + "license": "MIT", "dependencies": { - "@typescript-eslint/types": "7.9.0", + "@typescript-eslint/types": "8.4.0", "eslint-visitor-keys": "^3.4.3" }, "engines": { - "node": "^18.18.0 || >=20.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, - "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 - }, "node_modules/@vscode/test-electron": { "version": "2.3.10", "resolved": "https://registry.npmjs.org/@vscode/test-electron/-/test-electron-2.3.10.tgz", @@ -1550,10 +1636,11 @@ } }, "node_modules/acorn": { - "version": "8.11.3", - "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.11.3.tgz", - "integrity": "sha512-Y9rRfJG5jcKOE0CLisYbojUjIrIEE7AGMzA/Sm4BslANhbS+cDMpgBdcPT91oJ7OuJ9hYJBx59RjbhxVnrF8Xg==", + "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" }, @@ -1566,23 +1653,24 @@ "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, + "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "node_modules/ag-charts-types": { - "version": "10.0.1", - "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-10.0.1.tgz", - "integrity": "sha512-o8aXJfO5lsLGu4jE/2MiTogLCfdJ8UCmrWNPb+AWU0YutCrBHO0uWbSuqzabZxZ4WHxwwRtTllZMT6WqTdz+qg==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/ag-charts-types/-/ag-charts-types-10.1.0.tgz", + "integrity": "sha512-pk9ft8hbgTXJ/thI/SEUR1BoauNplYExpcHh7tMOqVikoDsta1O15TB1ZL4XWnl4TPIzROBmONKsz7d8a2HBuQ==", "license": "MIT" }, "node_modules/ag-grid-community": { - "version": "32.0.1", - "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-32.0.1.tgz", - "integrity": "sha512-/eimCgJqMeyFxpJMTQuCtedKzk+BIInqhRdKdoQG8MD3yjrs/AWQFAcT6MP0T64CuNd85mxwB2t+3Ggb+S8hdA==", + "version": "32.1.0", + "resolved": "https://registry.npmjs.org/ag-grid-community/-/ag-grid-community-32.1.0.tgz", + "integrity": "sha512-RVvkjRH61nuCXwIqTKQPqNbKR+8cGBKw7S1qmmMXsy0pCBAJaQn4kL3v31hKHxDtV4bPscBXLFKGnKzHuss0GQ==", "license": "MIT", "dependencies": { - "ag-charts-types": "10.0.1" + "ag-charts-types": "10.1.0" } }, "node_modules/agent-base": { @@ -1602,6 +1690,7 @@ "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.12.6.tgz", "integrity": "sha512-j3fVLgvTo527anyYyJOGTYJbG+vnnQYvE0m5mmkc1TK+nxAppkCLMIL0aZ4dblVCNoGShhm+kzE4ZUykBoMg4g==", "dev": true, + "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", @@ -1665,24 +1754,15 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "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, - "engines": { - "node": ">=8" - } - }, "node_modules/asynckit": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==" }, "node_modules/axios": { - "version": "1.7.4", - "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.4.tgz", - "integrity": "sha512-DukmaFRnY6AzAALSH4J2M3k6PkaC+MfaAGdEERRWcC9q3/TWQwLpHR8ZRLKTdQ3aBDL64EdluRDjJqKw+BPZEw==", + "version": "1.7.7", + "resolved": "https://registry.npmjs.org/axios/-/axios-1.7.7.tgz", + "integrity": "sha512-S4kL7XrjgBmvdGut0sN3yJxqYzrDOnivkBiN0OFs6hLiUam3UPvswUo0kqGyhqUZGEOytHyumEdXsAkgCOUf3Q==", "license": "MIT", "dependencies": { "follow-redirects": "^1.15.6", @@ -1794,6 +1874,7 @@ "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -1935,20 +2016,6 @@ "node": "*" } }, - "node_modules/csv-parser": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/csv-parser/-/csv-parser-3.0.0.tgz", - "integrity": "sha512-s6OYSXAK3IdKqYO33y09jhypG/bSDHPuyCme/IdEHfWpLf/jKcpitVFyOC6UemgGk8v7Q5u2XE0vvwmanxhGlQ==", - "dependencies": { - "minimist": "^1.2.0" - }, - "bin": { - "csv-parser": "bin/csv-parser" - }, - "engines": { - "node": ">= 10" - } - }, "node_modules/debug": { "version": "4.3.4", "resolved": "https://registry.npmjs.org/debug/-/debug-4.3.4.tgz", @@ -2000,30 +2067,6 @@ "node": ">=0.3.1" } }, - "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, - "dependencies": { - "path-type": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/doctrine": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", - "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", - "dev": true, - "dependencies": { - "esutils": "^2.0.2" - }, - "engines": { - "node": ">=6.0.0" - } - }, "node_modules/duplexer2": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/duplexer2/-/duplexer2-0.1.4.tgz", @@ -2037,7 +2080,8 @@ "version": "0.2.0", "resolved": "https://registry.npmjs.org/eastasianwidth/-/eastasianwidth-0.2.0.tgz", "integrity": "sha512-I88TYZWc9XiYHRQ4/3c5rjjfgkjhLyW2luGIheGERbNQ6OY7yTybanSpDXZa8y7VUP9YmDcYa+eyq4ca7iLqWA==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/emoji-regex": { "version": "8.0.0", @@ -2054,41 +2098,43 @@ } }, "node_modules/esbuild": { - "version": "0.19.12", - "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.19.12.tgz", - "integrity": "sha512-aARqgq8roFBj054KvQr5f1sFu0D65G+miZRCuJyJ0G13Zwx7vRar5Zhn2tkQNzIXcBrNVsv/8stehpj+GAjgbg==", + "version": "0.23.1", + "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.23.1.tgz", + "integrity": "sha512-VVNz/9Sa0bs5SELtn3f7qhJCDPCF5oMEl5cO9/SSinpE9hbPVvxbd572HH5AKiP7WD8INO53GgfDDhRjkylHEg==", "dev": true, "hasInstallScript": true, + "license": "MIT", "bin": { "esbuild": "bin/esbuild" }, "engines": { - "node": ">=12" + "node": ">=18" }, "optionalDependencies": { - "@esbuild/aix-ppc64": "0.19.12", - "@esbuild/android-arm": "0.19.12", - "@esbuild/android-arm64": "0.19.12", - "@esbuild/android-x64": "0.19.12", - "@esbuild/darwin-arm64": "0.19.12", - "@esbuild/darwin-x64": "0.19.12", - "@esbuild/freebsd-arm64": "0.19.12", - "@esbuild/freebsd-x64": "0.19.12", - "@esbuild/linux-arm": "0.19.12", - "@esbuild/linux-arm64": "0.19.12", - "@esbuild/linux-ia32": "0.19.12", - "@esbuild/linux-loong64": "0.19.12", - "@esbuild/linux-mips64el": "0.19.12", - "@esbuild/linux-ppc64": "0.19.12", - "@esbuild/linux-riscv64": "0.19.12", - "@esbuild/linux-s390x": "0.19.12", - "@esbuild/linux-x64": "0.19.12", - "@esbuild/netbsd-x64": "0.19.12", - "@esbuild/openbsd-x64": "0.19.12", - "@esbuild/sunos-x64": "0.19.12", - "@esbuild/win32-arm64": "0.19.12", - "@esbuild/win32-ia32": "0.19.12", - "@esbuild/win32-x64": "0.19.12" + "@esbuild/aix-ppc64": "0.23.1", + "@esbuild/android-arm": "0.23.1", + "@esbuild/android-arm64": "0.23.1", + "@esbuild/android-x64": "0.23.1", + "@esbuild/darwin-arm64": "0.23.1", + "@esbuild/darwin-x64": "0.23.1", + "@esbuild/freebsd-arm64": "0.23.1", + "@esbuild/freebsd-x64": "0.23.1", + "@esbuild/linux-arm": "0.23.1", + "@esbuild/linux-arm64": "0.23.1", + "@esbuild/linux-ia32": "0.23.1", + "@esbuild/linux-loong64": "0.23.1", + "@esbuild/linux-mips64el": "0.23.1", + "@esbuild/linux-ppc64": "0.23.1", + "@esbuild/linux-riscv64": "0.23.1", + "@esbuild/linux-s390x": "0.23.1", + "@esbuild/linux-x64": "0.23.1", + "@esbuild/netbsd-x64": "0.23.1", + "@esbuild/openbsd-arm64": "0.23.1", + "@esbuild/openbsd-x64": "0.23.1", + "@esbuild/sunos-x64": "0.23.1", + "@esbuild/win32-arm64": "0.23.1", + "@esbuild/win32-ia32": "0.23.1", + "@esbuild/win32-x64": "0.23.1" } }, "node_modules/escalade": { @@ -2113,43 +2159,40 @@ } }, "node_modules/eslint": { - "version": "8.57.0", - "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.0.tgz", - "integrity": "sha512-dZ6+mexnaTIbSBZWgou51U6OmzIhYM2VcNdtiTtI7qPNZm35Akpr0f6vtw3w1Kmn5PYo+tZVfh13WrhpS6oLqQ==", + "version": "9.10.0", + "resolved": "https://registry.npmjs.org/eslint/-/eslint-9.10.0.tgz", + "integrity": "sha512-Y4D0IgtBZfOcOUAIQTSXBKoNGfY0REGqHJG6+Q81vNippW5YlKjHFj4soMxamKK1NXHUWuBZTLdU3Km+L/pcHw==", "dev": true, + "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.0", - "@humanwhocodes/config-array": "^0.11.14", + "@eslint-community/regexpp": "^4.11.0", + "@eslint/config-array": "^0.18.0", + "@eslint/eslintrc": "^3.1.0", + "@eslint/js": "9.10.0", + "@eslint/plugin-kit": "^0.1.0", "@humanwhocodes/module-importer": "^1.0.1", + "@humanwhocodes/retry": "^0.3.0", "@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.3.2", - "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", - "eslint-scope": "^7.2.2", - "eslint-visitor-keys": "^3.4.3", - "espree": "^9.6.1", - "esquery": "^1.4.2", + "eslint-scope": "^8.0.2", + "eslint-visitor-keys": "^4.0.0", + "espree": "^10.1.0", + "esquery": "^1.5.0", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", - "file-entry-cache": "^6.0.1", + "file-entry-cache": "^8.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", "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.1.2", "natural-compare": "^1.4.0", @@ -2161,10 +2204,18 @@ "eslint": "bin/eslint.js" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { - "url": "https://opencollective.com/eslint" + "url": "https://eslint.org/donate" + }, + "peerDependencies": { + "jiti": "*" + }, + "peerDependenciesMeta": { + "jiti": { + "optional": true + } } }, "node_modules/eslint-plugin-header": { @@ -2186,19 +2237,14 @@ } }, "node_modules/eslint-plugin-unused-imports": { - "version": "3.2.0", - "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-3.2.0.tgz", - "integrity": "sha512-6uXyn6xdINEpxE1MtDjxQsyXB37lfyO2yKGVVgtD7WEWQGORSOZjgrD6hBhvGv4/SO+TOlS+UnC6JppRqbuwGQ==", + "version": "4.1.3", + "resolved": "https://registry.npmjs.org/eslint-plugin-unused-imports/-/eslint-plugin-unused-imports-4.1.3.tgz", + "integrity": "sha512-lqrNZIZjFMUr7P06eoKtQLwyVRibvG7N+LtfKtObYGizAAGrcqLkc3tDx+iAik2z7q0j/XI3ihjupIqxhFabFA==", "dev": true, - "dependencies": { - "eslint-rule-composer": "^0.3.0" - }, - "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" - }, + "license": "MIT", "peerDependencies": { - "@typescript-eslint/eslint-plugin": "6 - 7", - "eslint": "8" + "@typescript-eslint/eslint-plugin": "^8.0.0-0 || ^7.0.0 || ^6.0.0 || ^5.0.0", + "eslint": "^9.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "@typescript-eslint/eslint-plugin": { @@ -2206,26 +2252,18 @@ } } }, - "node_modules/eslint-rule-composer": { - "version": "0.3.0", - "resolved": "https://registry.npmjs.org/eslint-rule-composer/-/eslint-rule-composer-0.3.0.tgz", - "integrity": "sha512-bt+Sh8CtDmn2OajxvNO+BX7Wn4CIWMpTRm3MaiKPCQcnnlm0CS2mhui6QaoeQugs+3Kj2ESKEEGJUdVafwhiCg==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "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==", + "version": "8.0.2", + "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-8.0.2.tgz", + "integrity": "sha512-6E4xmrTw5wtxnLA5wYL3WDfhZ/1bUBGOXV0zQvVRDOtrR8D0p6W7fs3JweNYhwRYeGvd/1CKX2se0/2s7Q/nJA==", "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" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2253,6 +2291,19 @@ "concat-map": "0.0.1" } }, + "node_modules/eslint/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, "node_modules/eslint/node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", @@ -2278,17 +2329,31 @@ } }, "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==", + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/espree/-/espree-10.1.0.tgz", + "integrity": "sha512-M1M6CpiE6ffoigIOWYO9UDP8TMUw9kqb21tf+08IgDYjCsOvCuDt4jQcZmoYxx+w7zlKw9/N0KXfto+I8/FrXA==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { - "acorn": "^8.9.0", + "acorn": "^8.12.0", "acorn-jsx": "^5.3.2", - "eslint-visitor-keys": "^3.4.1" + "eslint-visitor-keys": "^4.0.0" }, "engines": { - "node": "^12.22.0 || ^14.17.0 || >=16.0.0" + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" + }, + "funding": { + "url": "https://opencollective.com/eslint" + } + }, + "node_modules/espree/node_modules/eslint-visitor-keys": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-4.0.0.tgz", + "integrity": "sha512-OtIRv/2GyiF6o/d8K7MYKKbXrOUBIK6SfkIRM4Z0dY3w+LiQ0vy3F57m0Z71bjbyeiWFiHJ8brqnmE6H6/jEuw==", + "dev": true, + "license": "Apache-2.0", + "engines": { + "node": "^18.18.0 || ^20.9.0 || >=21.1.0" }, "funding": { "url": "https://opencollective.com/eslint" @@ -2311,6 +2376,7 @@ "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, @@ -2332,6 +2398,7 @@ "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, + "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } @@ -2364,13 +2431,15 @@ "version": "3.1.3", "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", - "dev": true + "dev": true, + "license": "MIT" }, "node_modules/fast-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, + "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", @@ -2386,7 +2455,8 @@ "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" }, "node_modules/fast-levenshtein": { "version": "2.0.6", @@ -2412,15 +2482,16 @@ } }, "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==", + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-8.0.0.tgz", + "integrity": "sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==", "dev": true, + "license": "MIT", "dependencies": { - "flat-cache": "^3.0.4" + "flat-cache": "^4.0.0" }, "engines": { - "node": "^10.12.0 || >=12.0.0" + "node": ">=16.0.0" } }, "node_modules/fill-range": { @@ -2462,81 +2533,25 @@ } }, "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==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-4.0.1.tgz", + "integrity": "sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==", "dev": true, + "license": "MIT", "dependencies": { "flatted": "^3.2.9", - "keyv": "^4.5.3", - "rimraf": "^3.0.2" - }, - "engines": { - "node": "^10.12.0 || >=12.0.0" - } - }, - "node_modules/flat-cache/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, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/flat-cache/node_modules/glob": { - "version": "7.2.3", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", - "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", - "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" + "keyv": "^4.5.4" }, "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/flat-cache/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/flat-cache/node_modules/rimraf": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", - "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", - "dev": true, - "dependencies": { - "glob": "^7.1.3" - }, - "bin": { - "rimraf": "bin.js" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" + "node": ">=16" } }, "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 + "dev": true, + "license": "ISC" }, "node_modules/follow-redirects": { "version": "1.15.6", @@ -2558,10 +2573,11 @@ } }, "node_modules/foreground-child": { - "version": "3.1.1", - "resolved": "https://registry.npmjs.org/foreground-child/-/foreground-child-3.1.1.tgz", - "integrity": "sha512-TMKDUnIte6bfb5nWv7V/caI169OHgvwjb7V4WkeUvbQQdjr5rWKqHFiKWb/fcOwB+CzBT+qbWjvj+DVwRskpIg==", + "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, + "license": "ISC", "dependencies": { "cross-spawn": "^7.0.0", "signal-exit": "^4.0.1" @@ -2700,14 +2716,6 @@ "rimraf": "bin.js" } }, - "node_modules/fuse.js": { - "version": "7.0.0", - "resolved": "https://registry.npmjs.org/fuse.js/-/fuse.js-7.0.0.tgz", - "integrity": "sha512-14F4hBIxqKvD4Zz/XjDc3y94mNZN6pRv3U13Udo0lNLCWRBUsrMv2xwcF/y/Z5sV6+FQW+/ow68cHpm4sunt8Q==", - "engines": { - "node": ">=10" - } - }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", @@ -2775,35 +2783,13 @@ } }, "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, - "dependencies": { - "type-fest": "^0.20.2" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, - "node_modules/globby": { - "version": "11.1.0", - "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", - "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", + "version": "14.0.0", + "resolved": "https://registry.npmjs.org/globals/-/globals-14.0.0.tgz", + "integrity": "sha512-oahGvuMGQlPw/ivIYBjVSrWAfWLBeku5tpPE2fOPLi+WHffIWbuh2tCjhyQhTBPMf5E9jDEH4FOmTYgYwbKwtQ==", "dev": true, - "dependencies": { - "array-union": "^2.1.0", - "dir-glob": "^3.0.1", - "fast-glob": "^3.2.9", - "ignore": "^5.2.0", - "merge2": "^1.4.1", - "slash": "^3.0.0" - }, + "license": "MIT", "engines": { - "node": ">=10" + "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" @@ -2891,6 +2877,7 @@ "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.0.tgz", "integrity": "sha512-veYYhQa+D1QBKznvhUHxb8faxlrwUnxseDAbAp457E0wLNio2bOSKnjYDhMj+YiAq61xrMGhQk9iXVk5FzgQMw==", "dev": true, + "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" @@ -3238,15 +3225,16 @@ } }, "node_modules/jackspeak": { - "version": "2.3.6", - "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-2.3.6.tgz", - "integrity": "sha512-N3yCS/NegsOBokc8GAdM8UcmfsKiSS8cipheD/nivzr700H+nsMOxJjQnvwOcRYVuFkdH0wGUvW2WbXGmrZGbQ==", + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jackspeak/-/jackspeak-4.0.1.tgz", + "integrity": "sha512-cub8rahkh0Q/bw1+GxP7aeSe29hHHn2V4m29nnDlvCdlgU+3UGxkZp7Z53jLUdpX3jdTO0nJZUDl3xvbWc2Xog==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { "@isaacs/cliui": "^8.0.2" }, "engines": { - "node": ">=14" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3288,13 +3276,15 @@ "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 + "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" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", @@ -3344,6 +3334,7 @@ "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" } @@ -3386,32 +3377,35 @@ "dev": true }, "node_modules/lit": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lit/-/lit-3.1.3.tgz", - "integrity": "sha512-l4slfspEsnCcHVRTvaP7YnkTZEZggNFywLEIhQaGhYDczG+tu/vlgm/KaWIEjIp+ZyV20r2JnZctMb8LeLCG7Q==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lit/-/lit-3.2.0.tgz", + "integrity": "sha512-s6tI33Lf6VpDu7u4YqsSX78D28bYQulM+VAzsGch4fx2H0eLZnJsUBsPWmGYSGoKDNbjtRv02rio1o+UdPVwvw==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@lit/reactive-element": "^2.0.4", - "lit-element": "^4.0.4", - "lit-html": "^3.1.2" + "lit-element": "^4.1.0", + "lit-html": "^3.2.0" } }, "node_modules/lit-element": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.0.5.tgz", - "integrity": "sha512-iTWskWZEtn9SyEf4aBG6rKT8GABZMrTWop1+jopsEOgEcugcXJGKuX5bEbkq9qfzY+XB4MAgCaSPwnNpdsNQ3Q==", + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/lit-element/-/lit-element-4.1.0.tgz", + "integrity": "sha512-gSejRUQJuMQjV2Z59KAS/D4iElUhwKpIyJvZ9w+DIagIQjfJnhR20h2Q5ddpzXGS+fF0tMZ/xEYGMnKmaI/iww==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@lit-labs/ssr-dom-shim": "^1.2.0", "@lit/reactive-element": "^2.0.4", - "lit-html": "^3.1.2" + "lit-html": "^3.2.0" } }, "node_modules/lit-html": { - "version": "3.1.3", - "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.1.3.tgz", - "integrity": "sha512-FwIbqDD8O/8lM4vUZ4KvQZjPPNx7V1VhT7vmRB8RBAO0AU6wuTVdoXiu2CivVjEGdugvcbPNBLtPE1y0ifplHA==", + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/lit-html/-/lit-html-3.2.0.tgz", + "integrity": "sha512-pwT/HwoxqI9FggTrYVarkBKFN9MlTUpLrDHubTmW4SrkL3kkqW5gxwbxMMUnbbRHBC0WTZnYHcjDSCM559VyfA==", "dev": true, + "license": "BSD-3-Clause", "dependencies": { "@types/trusted-types": "^2.0.2" } @@ -3485,12 +3479,13 @@ } }, "node_modules/lru-cache": { - "version": "10.2.2", - "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.2.2.tgz", - "integrity": "sha512-9hp3Vp2/hFQUiIwKo8XCeFVnrg8Pk3TYNPIR7tJADKi5YfcF7vEaK7avFHTlSy3kOKYaJQaalfEo6YuXdceBOQ==", + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.0.1.tgz", + "integrity": "sha512-CgeuL5uom6j/ZVrg7G/+1IXqRY8JXX4Hghfy5YE0EhoYQWvndP1kufu58cmZLNIDKnRhZrXfdS9urVWx98AipQ==", "dev": true, + "license": "ISC", "engines": { - "node": "14 || >=16.14" + "node": "20 || >=22" } }, "node_modules/make-dir": { @@ -3531,17 +3526,19 @@ "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", "dev": true, + "license": "MIT", "engines": { "node": ">= 8" } }, "node_modules/micromatch": { - "version": "4.0.5", - "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.5.tgz", - "integrity": "sha512-DMy+ERcEW2q8Z2Po+WNXuw3c5YaUSFjAO5GsJqfEl7UjvtIuFKO6ZrKvcItdy98dwFI2N1tg3zNIdKaQT+aNdA==", + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", "dev": true, + "license": "MIT", "dependencies": { - "braces": "^3.0.2", + "braces": "^3.0.3", "picomatch": "^2.3.1" }, "engines": { @@ -3586,15 +3583,17 @@ "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.1", - "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.1.tgz", - "integrity": "sha512-UZ7eQ+h8ywIRAW1hIEl2AqdwzJucU/Kp59+8kkZeSvafXhZjul247BvIJjEVFVeON6d7lM46XX1HXCduKAS8VA==", + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.2.tgz", + "integrity": "sha512-qOOzS1cBTWYF4BH8fVePDBOO9iptMnGUEZwNc/cMWnTV2nVLZ7VoNWEPHkYczZA0pdoA7dl6e7FL659nX9S2aw==", "dev": true, + "license": "ISC", "engines": { "node": ">=16 || 14 >=14.17" } @@ -3903,6 +3902,13 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/package-json-from-dist": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/package-json-from-dist/-/package-json-from-dist-1.0.0.tgz", + "integrity": "sha512-dATvCeZN/8wQsGywez1mzHtTlP22H8OEfPrVMLNr4/eGa+ijtLn/6M5f0dY8UKNrC2O9UCU6SSoG3qRKnt7STw==", + "dev": true, + "license": "BlueOak-1.0.0" + }, "node_modules/pako": { "version": "1.0.11", "resolved": "https://registry.npmjs.org/pako/-/pako-1.0.11.tgz", @@ -3914,6 +3920,7 @@ "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, + "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, @@ -3949,16 +3956,17 @@ } }, "node_modules/path-scurry": { - "version": "1.11.1", - "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-1.11.1.tgz", - "integrity": "sha512-Xa4Nw17FS9ApQFJ9umLiJS4orGjm7ZzwUrwamcGQuHSzDyth9boKDaycYdDcZDuqYATXw4HFXgaqWTctW/v1HA==", + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.0.tgz", + "integrity": "sha512-ypGJsmGtdXUOeM5u93TyeIEfEhM6s+ljAhrk5vAvSx8uyY/02OvrZnA0YNGUrPXfpJMgI1ODd3nwz8Npx4O4cg==", "dev": true, + "license": "BlueOak-1.0.0", "dependencies": { - "lru-cache": "^10.2.0", - "minipass": "^5.0.0 || ^6.0.2 || ^7.0.0" + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" }, "engines": { - "node": ">=16 || 14 >=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -3970,15 +3978,6 @@ "integrity": "sha512-GQX3SSMokngb36+whdpRXE+3f9V8UzyAorlYvOGx87ufGHehNTn5lCxrKtLyZ4Yl/wEKnNnr98ZzOwwDZV5ogw==", "dev": true }, - "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, - "engines": { - "node": ">=8" - } - }, "node_modules/pend": { "version": "1.2.0", "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", @@ -4032,10 +4031,11 @@ } }, "node_modules/prettier": { - "version": "3.2.5", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.2.5.tgz", - "integrity": "sha512-3/GWa9aOC0YeD7LUfvOG2NiDyhOWRvt1k+rcKhOuYnMY24iiCphgneUfJDyFXd6rZCAnuLBv6UeAULtrhT/F4A==", + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.3.3.tgz", + "integrity": "sha512-i2tDNA0O5IrMO757lfrdQZCc2jPNDVntV0m/+4whiDfWaTKfMNgR7Qz0NAeGz/nRqF4m5/6CLzbP4/liHt12Ew==", "dev": true, + "license": "MIT", "bin": { "prettier": "bin/prettier.cjs" }, @@ -4084,6 +4084,7 @@ "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, + "license": "MIT", "engines": { "node": ">=6" } @@ -4184,6 +4185,7 @@ "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, + "license": "MIT", "engines": { "node": ">=4" } @@ -4199,40 +4201,60 @@ } }, "node_modules/rimraf": { - "version": "5.0.7", - "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-5.0.7.tgz", - "integrity": "sha512-nV6YcJo5wbLW77m+8KjH8aB/7/rxQy9SZ0HY5shnwULfS+9nmTtVXAJET5NdZmCzA4fPI/Hm1wo/Po/4mopOdg==", + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-6.0.1.tgz", + "integrity": "sha512-9dkvaxAsk/xNXSJzMgFqqMCuFgt2+KsOFek3TMLfo8NCPfWpBmqwyNn5Y+NX56QUYfCtsyhF3ayiboEoUmJk/A==", "dev": true, + "license": "ISC", "dependencies": { - "glob": "^10.3.7" + "glob": "^11.0.0", + "package-json-from-dist": "^1.0.0" }, "bin": { "rimraf": "dist/esm/bin.mjs" }, "engines": { - "node": ">=14.18" + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/rimraf/node_modules/glob": { - "version": "10.3.15", - "resolved": "https://registry.npmjs.org/glob/-/glob-10.3.15.tgz", - "integrity": "sha512-0c6RlJt1TICLyvJYIApxb8GsXoai0KUP7AxKKAtsYXdgJR1mGEUa7DgwShbdk1nly0PYoZj01xd4hzbq3fsjpw==", + "version": "11.0.0", + "resolved": "https://registry.npmjs.org/glob/-/glob-11.0.0.tgz", + "integrity": "sha512-9UiX/Bl6J2yaBbxKoEBRm4Cipxgok8kQYcOPEhScPwebu2I0HoQOuYdIO6S3hLuWoZgpDpwQZMzTFxgpkyT76g==", "dev": true, + "license": "ISC", "dependencies": { "foreground-child": "^3.1.0", - "jackspeak": "^2.3.6", - "minimatch": "^9.0.1", - "minipass": "^7.0.4", - "path-scurry": "^1.11.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": ">=16 || 14 >=14.18" + "node": "20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/rimraf/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, + "license": "ISC", + "dependencies": { + "brace-expansion": "^2.0.1" + }, + "engines": { + "node": "20 || >=22" }, "funding": { "url": "https://github.com/sponsors/isaacs" @@ -4319,6 +4341,7 @@ "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-4.1.0.tgz", "integrity": "sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==", "dev": true, + "license": "ISC", "engines": { "node": ">=14" }, @@ -4359,15 +4382,6 @@ "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, - "node_modules/slash": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", - "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", @@ -4406,6 +4420,7 @@ "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", "dev": true, + "license": "MIT", "dependencies": { "emoji-regex": "^8.0.0", "is-fullwidth-code-point": "^3.0.0", @@ -4433,6 +4448,7 @@ "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", "dev": true, + "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, @@ -4516,6 +4532,7 @@ "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.3.0.tgz", "integrity": "sha512-UQMIo7pb8WRomKR1/+MFVLTroIvDVtMX3K6OUir8ynLyzB8Jeriont2bTAtmNPa1ekAgN7YPDyf6V+ygrdU+eQ==", "dev": true, + "license": "MIT", "engines": { "node": ">=16" }, @@ -4549,18 +4566,6 @@ "node": ">=4" } }, - "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, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/sponsors/sindresorhus" - } - }, "node_modules/typescript": { "version": "5.4.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.4.5.tgz", @@ -4611,6 +4616,7 @@ "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, + "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" } @@ -4916,6 +4922,7 @@ "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, + "license": "MIT", "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", diff --git a/package.json b/package.json index 9fbee1f5..8b958450 100644 --- a/package.json +++ b/package.json @@ -3,7 +3,7 @@ "displayName": "kdb", "description": "IDE support for kdb product suite including the q programming language", "publisher": "KX", - "version": "1.7.0", + "version": "1.8.0", "engines": { "vscode": "^1.86.0" }, @@ -165,6 +165,10 @@ "type": "string", "description": "QHOME directory for q runtime" }, + "kdb.neverShowQInstallAgain": { + "type": "boolean", + "description": "Never show q install walkthrough again" + }, "kdb.hideSubscribeRegistrationNotification": { "type": "boolean", "description": "Hide subscribe for registration notification", @@ -175,6 +179,15 @@ "description": "Enable linting for q and quke files", "default": false }, + "kdb.refactoring": { + "type": "string", + "enum": [ + "Workspace", + "Window" + ], + "description": "Enable refactoring across files", + "default": "Workspace" + }, "kdb.connectionMap": { "type": "object", "description": "Connection map for workspace files", @@ -196,6 +209,21 @@ } }, "commands": [ + { + "category": "KX", + "command": "kdb.connections.export.all", + "title": "Export connections" + }, + { + "category": "KX", + "command": "kdb.connections.export.single", + "title": "Export connection" + }, + { + "category": "KX", + "command": "kdb.connections.import", + "title": "Import connections" + }, { "category": "KX", "command": "kdb.refreshServerObjects", @@ -236,6 +264,15 @@ }, "enablement": "workspaceFolderCount > 0" }, + { + "category": "KX", + "command": "kdb.connection.content.selectView", + "title": "View contents", + "icon": { + "dark": "./resources/select-view.svg", + "light": "./resources/select-view.svg" + } + }, { "category": "KX", "command": "kdb.refreshScratchpadExplorer", @@ -632,6 +669,14 @@ { "command": "kdb.enableTLS", "when": "false" + }, + { + "command": "kdb.connections.export.single", + "when": "false" + }, + { + "command": "kdb.connection.content.selectView", + "when": "false" } ], "webview/context": [ @@ -697,6 +742,16 @@ "command": "kdb.resultsPanel.export.csv", "when": "view == kdb-results", "group": "resultsPanel" + }, + { + "command": "kdb.connections.export.all", + "when": "view == kdb-servers", + "group": "inline" + }, + { + "command": "kdb.connections.import", + "when": "view == kdb-servers", + "group": "inline" } ], "view/item/context": [ @@ -745,6 +800,11 @@ "when": "view == kdb-servers && viewItem in kdb.rootNodes", "group": "connection@5" }, + { + "command": "kdb.connections.export.single", + "when": "view == kdb-servers && (viewItem in kdb.rootNodes || viewItem in kdb.insightsNodes)", + "group": "connection@6" + }, { "command": "kdb.startLocalProcess", "when": "view == kdb-servers && viewItem in kdb.local && viewItem not in kdb.running && viewItem in kdb.rootNodes", @@ -784,6 +844,11 @@ "command": "kdb.deleteLabel", "when": "view == kdb-servers && viewItem == label", "group": "label@3" + }, + { + "command": "kdb.connection.content.selectView", + "when": "view == kdb-servers && viewItem in kdb.selectContentNodesContext", + "group": "inline" } ], "editor/title/run": [ @@ -913,27 +978,27 @@ "@types/sinon": "^17.0.3", "@types/vscode": "^1.86.0", "@types/vscode-webview": "^1.57.5", - "@typescript-eslint/eslint-plugin": "^7.9.0", - "@typescript-eslint/parser": "^7.9.0", + "@typescript-eslint/eslint-plugin": "^8.4.0", + "@typescript-eslint/parser": "^8.4.0", "@vscode/test-electron": "^2.3.10", - "esbuild": "^0.19.12", - "eslint": "^8.57.0", + "esbuild": "^0.23.1", + "eslint": "^9.10.0", "eslint-plugin-header": "^3.1.1", "eslint-plugin-license-header": "^0.6.1", - "eslint-plugin-unused-imports": "^3.2.0", + "eslint-plugin-unused-imports": "^4.1.3", "glob": "^8.1.0", "istanbul-lib-coverage": "^3.2.2", "istanbul-lib-instrument": "^3.3.0", "istanbul-lib-report": "^2.0.8", "istanbul-lib-source-maps": "^3.0.6", "istanbul-reports": "^3.1.7", - "lit": "^3.1.3", + "lit": "^3.2.0", "mocha": "^10.4.0", "mocha-junit-reporter": "^2.2.1", "mocha-multi-reporters": "^1.5.1", "mock-fs": "^5.2.0", - "prettier": "^3.2.5", - "rimraf": "^5.0.7", + "prettier": "^3.3.3", + "rimraf": "^6.0.1", "sinon": "^17.0.1", "typescript": "^5.4.5", "vscode-dts": "^0.3.3", @@ -943,16 +1008,13 @@ "vscode-test": "^1.6.1" }, "dependencies": { - "@types/graceful-fs": "^4.1.9", "@vscode/webview-ui-toolkit": "^1.4.0", "@windozer/node-q": "^2.6.0", - "ag-grid-community": "^32.0.1", - "axios": "^1.7.4", + "ag-grid-community": "^32.1.0", + "axios": "^1.7.7", "chevrotain": "^10.5.0", - "csv-parser": "^3.0.0", "extract-zip": "^2.0.1", "fs-extra": "^11.2.0", - "fuse.js": "^7.0.0", "jwt-decode": "^4.0.0", "moment": "^2.30.1", "moment-duration-format": "^2.3.2", diff --git a/resources/dark/aggicon.svg b/resources/dark/aggicon.svg index 4834b95f..1db75480 100644 --- a/resources/dark/aggicon.svg +++ b/resources/dark/aggicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/apiicon.svg b/resources/dark/apiicon.svg index 2b71c797..534e4466 100644 --- a/resources/dark/apiicon.svg +++ b/resources/dark/apiicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/p-insights-active.svg b/resources/dark/conn-insights-active.svg similarity index 96% rename from resources/dark/p-insights-active.svg rename to resources/dark/conn-insights-active.svg index 0c2b46d5..bfbaa150 100644 --- a/resources/dark/p-insights-active.svg +++ b/resources/dark/conn-insights-active.svg @@ -1,7 +1,7 @@ - + - + + fill="#BB8B2D" /> - - \ No newline at end of file + \ No newline at end of file diff --git a/resources/dark/conn-insights.svg b/resources/dark/conn-insights.svg new file mode 100644 index 00000000..6bb27189 --- /dev/null +++ b/resources/dark/conn-insights.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/resources/dark/p-q-connection-active.svg b/resources/dark/conn-kdb-active.svg similarity index 96% rename from resources/dark/p-q-connection-active.svg rename to resources/dark/conn-kdb-active.svg index c9d2dc57..e70df527 100644 --- a/resources/dark/p-q-connection-active.svg +++ b/resources/dark/conn-kdb-active.svg @@ -1,12 +1,12 @@ - + + fill="#0FBC7A" /> \ No newline at end of file diff --git a/resources/dark/p-q-connection-connected.svg b/resources/dark/conn-kdb-connected.svg similarity index 96% rename from resources/dark/p-q-connection-connected.svg rename to resources/dark/conn-kdb-connected.svg index 52e7b21c..271f104e 100644 --- a/resources/dark/p-q-connection-connected.svg +++ b/resources/dark/conn-kdb-connected.svg @@ -1,12 +1,12 @@ - + + fill="#BB8B2D" /> \ No newline at end of file diff --git a/resources/dark/p-q-connection.svg b/resources/dark/conn-kdb.svg similarity index 97% rename from resources/dark/p-q-connection.svg rename to resources/dark/conn-kdb.svg index 28ec8d60..b2f0fb21 100644 --- a/resources/dark/p-q-connection.svg +++ b/resources/dark/conn-kdb.svg @@ -1,7 +1,7 @@ - + - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/datasource-connected.svg b/resources/dark/datasource-connected.svg index 7a23bdd2..f19a987e 100644 --- a/resources/dark/datasource-connected.svg +++ b/resources/dark/datasource-connected.svg @@ -1,6 +1,6 @@ + fill="#BB8B2D" /> \ No newline at end of file diff --git a/resources/dark/dictionaries.svg b/resources/dark/dictionaries.svg index 52edcfe5..14b529f4 100644 --- a/resources/dark/dictionaries.svg +++ b/resources/dark/dictionaries.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/functions.svg b/resources/dark/functions.svg index 713fc615..1d422755 100644 --- a/resources/dark/functions.svg +++ b/resources/dark/functions.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/labels/label-red.svg b/resources/dark/labels/label-red.svg index f1ea352c..fc39dccd 100644 --- a/resources/dark/labels/label-red.svg +++ b/resources/dark/labels/label-red.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/resources/dark/metaicon.svg b/resources/dark/metaicon.svg index 2b95a430..f32884ee 100644 --- a/resources/dark/metaicon.svg +++ b/resources/dark/metaicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/namespaces.svg b/resources/dark/namespaces.svg index 5e830bbc..e41e6974 100644 --- a/resources/dark/namespaces.svg +++ b/resources/dark/namespaces.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/p-data-active.svg b/resources/dark/p-data-active.svg deleted file mode 100644 index a90a9332..00000000 --- a/resources/dark/p-data-active.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/dark/p-data-connected.svg b/resources/dark/p-data-connected.svg deleted file mode 100644 index 8104c79a..00000000 --- a/resources/dark/p-data-connected.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/dark/p-data.svg b/resources/dark/p-data.svg deleted file mode 100644 index ff1b6b63..00000000 --- a/resources/dark/p-data.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/dark/p-dictionary.svg b/resources/dark/p-dictionary.svg deleted file mode 100644 index 8ce7c941..00000000 --- a/resources/dark/p-dictionary.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-file.svg b/resources/dark/p-file.svg deleted file mode 100644 index d0f7ecad..00000000 --- a/resources/dark/p-file.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-folder.svg b/resources/dark/p-folder.svg deleted file mode 100644 index 75724e56..00000000 --- a/resources/dark/p-folder.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-function.svg b/resources/dark/p-function.svg deleted file mode 100644 index c11359f8..00000000 --- a/resources/dark/p-function.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-insights.svg b/resources/dark/p-insights.svg deleted file mode 100644 index 7aaeed6f..00000000 --- a/resources/dark/p-insights.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/resources/dark/p-table.svg b/resources/dark/p-table.svg deleted file mode 100644 index a58eca87..00000000 --- a/resources/dark/p-table.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-var.svg b/resources/dark/p-var.svg deleted file mode 100644 index 77fa34ef..00000000 --- a/resources/dark/p-var.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/p-view.svg b/resources/dark/p-view.svg deleted file mode 100644 index f02090f2..00000000 --- a/resources/dark/p-view.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/dark/packageicon.svg b/resources/dark/packageicon.svg index ba7f543a..eacb6fe4 100644 --- a/resources/dark/packageicon.svg +++ b/resources/dark/packageicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/python-connected.svg b/resources/dark/python-connected.svg index def2a249..eadbdaf1 100644 --- a/resources/dark/python-connected.svg +++ b/resources/dark/python-connected.svg @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/resources/dark/rcicon.svg b/resources/dark/rcicon.svg index 4d546120..a0bcdc9d 100644 --- a/resources/dark/rcicon.svg +++ b/resources/dark/rcicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/refresh.svg b/resources/dark/refresh.svg index a3b29dfc..fa7354fe 100644 --- a/resources/dark/refresh.svg +++ b/resources/dark/refresh.svg @@ -1 +1,4 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/resources/dark/schemaicon.svg b/resources/dark/schemaicon.svg index 86d82b46..871b0083 100644 --- a/resources/dark/schemaicon.svg +++ b/resources/dark/schemaicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/scratchpad-connected.svg b/resources/dark/scratchpad-connected.svg index 54d62f00..d254c68e 100644 --- a/resources/dark/scratchpad-connected.svg +++ b/resources/dark/scratchpad-connected.svg @@ -3,12 +3,12 @@ xmlns="http://www.w3.org/2000/svg"> \ No newline at end of file diff --git a/resources/dark/tables.svg b/resources/dark/tables.svg index c931182b..6b66b1de 100644 --- a/resources/dark/tables.svg +++ b/resources/dark/tables.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/variables.svg b/resources/dark/variables.svg index f63d3082..0943e602 100644 --- a/resources/dark/variables.svg +++ b/resources/dark/variables.svg @@ -1,8 +1,9 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/dark/views.svg b/resources/dark/views.svg index f0cc49b6..1462e64b 100644 --- a/resources/dark/views.svg +++ b/resources/dark/views.svg @@ -1,8 +1,10 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/kx_logo-dark.svg b/resources/kx_logo-dark.svg index 53f1b9ea..4cadd7bd 100644 --- a/resources/kx_logo-dark.svg +++ b/resources/kx_logo-dark.svg @@ -5,7 +5,9 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 66.2 33.8" class="menu_logo" version="1.1" id="svg8" sodipodi:docname="kx.svg" inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"> + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 66.2 33.8" + class="menu_logo" version="1.1" id="svg8" sodipodi:docname="kx.svg" + inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"> @@ -15,13 +17,22 @@ - + - - - + + + diff --git a/resources/kx_logo.svg b/resources/kx_logo.svg index 79300416..7a3ac86d 100644 --- a/resources/kx_logo.svg +++ b/resources/kx_logo.svg @@ -5,7 +5,9 @@ xmlns:svg="http://www.w3.org/2000/svg" xmlns="http://www.w3.org/2000/svg" xmlns:sodipodi="http://sodipodi.sourceforge.net/DTD/sodipodi-0.dtd" - xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 66.2 33.8" class="menu_logo" version="1.1" id="svg8" sodipodi:docname="kx.svg" inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"> + xmlns:inkscape="http://www.inkscape.org/namespaces/inkscape" viewBox="0 0 66.2 33.8" + class="menu_logo" version="1.1" id="svg8" sodipodi:docname="kx.svg" + inkscape:version="1.0.2-2 (e86c870879, 2021-01-15)"> @@ -15,13 +17,22 @@ - + - - - + + + diff --git a/resources/light/aggicon.svg b/resources/light/aggicon.svg index 81d31a44..83e0b738 100644 --- a/resources/light/aggicon.svg +++ b/resources/light/aggicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/apiicon.svg b/resources/light/apiicon.svg index ae9919c1..1ee674f3 100644 --- a/resources/light/apiicon.svg +++ b/resources/light/apiicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/p-insights-active.svg b/resources/light/conn-insights-active.svg similarity index 100% rename from resources/light/p-insights-active.svg rename to resources/light/conn-insights-active.svg diff --git a/resources/dark/p-insights-connected.svg b/resources/light/conn-insights-connected.svg similarity index 95% rename from resources/dark/p-insights-connected.svg rename to resources/light/conn-insights-connected.svg index 748c61d1..6cbdf11f 100644 --- a/resources/dark/p-insights-connected.svg +++ b/resources/light/conn-insights-connected.svg @@ -1,12 +1,12 @@ - + + fill="#BB8B2D" /> \ No newline at end of file diff --git a/resources/light/conn-insights.svg b/resources/light/conn-insights.svg new file mode 100644 index 00000000..dfa80774 --- /dev/null +++ b/resources/light/conn-insights.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/resources/light/p-q-connection-active.svg b/resources/light/conn-kdb-active.svg similarity index 100% rename from resources/light/p-q-connection-active.svg rename to resources/light/conn-kdb-active.svg diff --git a/resources/light/p-q-connection-connected.svg b/resources/light/conn-kdb-connected.svg similarity index 96% rename from resources/light/p-q-connection-connected.svg rename to resources/light/conn-kdb-connected.svg index 52e7b21c..d2f2a70a 100644 --- a/resources/light/p-q-connection-connected.svg +++ b/resources/light/conn-kdb-connected.svg @@ -1,12 +1,12 @@ - + + fill="#BB8B2D" /> \ No newline at end of file diff --git a/resources/light/p-q-connection.svg b/resources/light/conn-kdb.svg similarity index 100% rename from resources/light/p-q-connection.svg rename to resources/light/conn-kdb.svg diff --git a/resources/light/dapicon.svg b/resources/light/dapicon.svg index f2c6e9a6..9b4e13fc 100644 --- a/resources/light/dapicon.svg +++ b/resources/light/dapicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/datasource-connected.svg b/resources/light/datasource-connected.svg index 7a23bdd2..f19a987e 100644 --- a/resources/light/datasource-connected.svg +++ b/resources/light/datasource-connected.svg @@ -1,6 +1,6 @@ + fill="#BB8B2D" /> \ No newline at end of file diff --git a/resources/light/dictionaries.svg b/resources/light/dictionaries.svg index ee540e3e..cc1d092f 100644 --- a/resources/light/dictionaries.svg +++ b/resources/light/dictionaries.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/functions.svg b/resources/light/functions.svg index 2e0ed2c8..281eb682 100644 --- a/resources/light/functions.svg +++ b/resources/light/functions.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/labels/label-red.svg b/resources/light/labels/label-red.svg index f1ea352c..fc39dccd 100644 --- a/resources/light/labels/label-red.svg +++ b/resources/light/labels/label-red.svg @@ -1,3 +1,5 @@ - - + + \ No newline at end of file diff --git a/resources/light/metaicon.svg b/resources/light/metaicon.svg index aa8a41f4..95aa9b4d 100644 --- a/resources/light/metaicon.svg +++ b/resources/light/metaicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/namespaces.svg b/resources/light/namespaces.svg index 485287ce..04bce11f 100644 --- a/resources/light/namespaces.svg +++ b/resources/light/namespaces.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/p-data-active.svg b/resources/light/p-data-active.svg deleted file mode 100644 index a90a9332..00000000 --- a/resources/light/p-data-active.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/light/p-data-connected.svg b/resources/light/p-data-connected.svg deleted file mode 100644 index 8104c79a..00000000 --- a/resources/light/p-data-connected.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/light/p-data.svg b/resources/light/p-data.svg deleted file mode 100644 index ff1b6b63..00000000 --- a/resources/light/p-data.svg +++ /dev/null @@ -1,9 +0,0 @@ - - - - - - - - - \ No newline at end of file diff --git a/resources/light/p-dictionary.svg b/resources/light/p-dictionary.svg deleted file mode 100644 index 8ce7c941..00000000 --- a/resources/light/p-dictionary.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-file.svg b/resources/light/p-file.svg deleted file mode 100644 index d0f7ecad..00000000 --- a/resources/light/p-file.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-folder.svg b/resources/light/p-folder.svg deleted file mode 100644 index 75724e56..00000000 --- a/resources/light/p-folder.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-function.svg b/resources/light/p-function.svg deleted file mode 100644 index c11359f8..00000000 --- a/resources/light/p-function.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-insights.svg b/resources/light/p-insights.svg deleted file mode 100644 index 31e0d520..00000000 --- a/resources/light/p-insights.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - \ No newline at end of file diff --git a/resources/light/p-table.svg b/resources/light/p-table.svg deleted file mode 100644 index a58eca87..00000000 --- a/resources/light/p-table.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-var.svg b/resources/light/p-var.svg deleted file mode 100644 index 77fa34ef..00000000 --- a/resources/light/p-var.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/p-view.svg b/resources/light/p-view.svg deleted file mode 100644 index f02090f2..00000000 --- a/resources/light/p-view.svg +++ /dev/null @@ -1 +0,0 @@ -icon \ No newline at end of file diff --git a/resources/light/packageicon.svg b/resources/light/packageicon.svg index bf745c96..fbf62f06 100644 --- a/resources/light/packageicon.svg +++ b/resources/light/packageicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/python-connected.svg b/resources/light/python-connected.svg index def2a249..eadbdaf1 100644 --- a/resources/light/python-connected.svg +++ b/resources/light/python-connected.svg @@ -1,6 +1,6 @@ - - \ No newline at end of file diff --git a/resources/light/rcicon.svg b/resources/light/rcicon.svg index 157a4f4a..eabb9475 100644 --- a/resources/light/rcicon.svg +++ b/resources/light/rcicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/refresh.svg b/resources/light/refresh.svg index 91960156..72d8896a 100644 --- a/resources/light/refresh.svg +++ b/resources/light/refresh.svg @@ -1 +1,5 @@ - \ No newline at end of file + + + \ No newline at end of file diff --git a/resources/light/schemaicon.svg b/resources/light/schemaicon.svg index edb7a75f..e87068b0 100644 --- a/resources/light/schemaicon.svg +++ b/resources/light/schemaicon.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/scratchpad-connected.svg b/resources/light/scratchpad-connected.svg index 54d62f00..d254c68e 100644 --- a/resources/light/scratchpad-connected.svg +++ b/resources/light/scratchpad-connected.svg @@ -3,12 +3,12 @@ xmlns="http://www.w3.org/2000/svg"> \ No newline at end of file diff --git a/resources/light/tables.svg b/resources/light/tables.svg index b959f149..99db5406 100644 --- a/resources/light/tables.svg +++ b/resources/light/tables.svg @@ -1,8 +1,11 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/variables.svg b/resources/light/variables.svg index 1523ff80..289b7477 100644 --- a/resources/light/variables.svg +++ b/resources/light/variables.svg @@ -1,8 +1,9 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/light/views.svg b/resources/light/views.svg index 3470188c..1652eb6e 100644 --- a/resources/light/views.svg +++ b/resources/light/views.svg @@ -1,8 +1,10 @@ - - - - - - - + + + + + + + \ No newline at end of file diff --git a/resources/select-view.svg b/resources/select-view.svg new file mode 100644 index 00000000..06235de8 --- /dev/null +++ b/resources/select-view.svg @@ -0,0 +1,15 @@ + + + + + + + + + + \ No newline at end of file diff --git a/resources/server.svg b/resources/server.svg index 0ff640ba..6241fa9d 100644 --- a/resources/server.svg +++ b/resources/server.svg @@ -1 +1,17 @@ -Icon-intune-331 \ No newline at end of file + + + + + + + + + + + Icon-intune-331 + + \ No newline at end of file diff --git a/server/src/qLangServer.ts b/server/src/qLangServer.ts index 0529e1e8..ea13469f 100644 --- a/server/src/qLangServer.ts +++ b/server/src/qLangServer.ts @@ -13,6 +13,12 @@ import { Position, TextDocument } from "vscode-languageserver-textdocument"; import { + CallHierarchyIncomingCall, + CallHierarchyIncomingCallsParams, + CallHierarchyItem, + CallHierarchyOutgoingCall, + CallHierarchyOutgoingCallsParams, + CallHierarchyPrepareParams, CompletionItem, CompletionItemKind, CompletionParams, @@ -21,8 +27,10 @@ import { Diagnostic, DiagnosticSeverity, DidChangeConfigurationParams, + DidChangeWatchedFilesParams, DocumentSymbol, DocumentSymbolParams, + FileChangeType, InitializeParams, LSPAny, Location, @@ -41,6 +49,8 @@ import { TextEdit, WorkspaceEdit, } from "vscode-languageserver/node"; +import { sync as glob } from "glob"; +import { fileURLToPath, pathToFileURL } from "node:url"; import { FindKind, Token, @@ -63,24 +73,37 @@ import { RCurly, } from "./parser"; import { lint } from "./linter"; +import { readFileSync } from "node:fs"; interface Settings { debug: boolean; linting: boolean; + refactoring: "Workspace" | "Window"; } -const defaultSettings: Settings = { debug: false, linting: false }; +const defaultSettings: Settings = { + debug: false, + linting: false, + refactoring: "Workspace", +}; + +interface Tokenized { + uri: string; + tokens: Token[]; +} export default class QLangServer { private declare connection: Connection; private declare params: InitializeParams; private declare settings: Settings; + private declare cached: Map; public declare documents: TextDocuments; constructor(connection: Connection, params: InitializeParams) { this.connection = connection; this.params = params; this.settings = defaultSettings; + this.cached = new Map(); this.documents = new TextDocuments(TextDocument); this.documents.listen(this.connection); this.documents.onDidClose(this.onDidClose.bind(this)); @@ -90,6 +113,18 @@ export default class QLangServer { this.connection.onDefinition(this.onDefinition.bind(this)); this.connection.onRenameRequest(this.onRenameRequest.bind(this)); this.connection.onCompletion(this.onCompletion.bind(this)); + this.connection.onDidChangeWatchedFiles( + this.onDidChangeWatchedFiles.bind(this), + ); + this.connection.languages.callHierarchy.onPrepare( + this.onPrepareCallHierarchy.bind(this), + ); + this.connection.languages.callHierarchy.onIncomingCalls( + this.onIncomingCallsCallHierarchy.bind(this), + ); + this.connection.languages.callHierarchy.onOutgoingCalls( + this.onOutgoingCallsCallHierarchy.bind(this), + ); this.connection.onDidChangeConfiguration( this.onDidChangeConfiguration.bind(this), ); @@ -113,25 +148,40 @@ export default class QLangServer { renameProvider: true, completionProvider: { resolveProvider: false }, selectionRangeProvider: true, + callHierarchyProvider: true, }; } public setSettings(settings: LSPAny) { - this.settings = settings; + this.settings = { + debug: settings.debug || false, + linting: settings.linting || false, + refactoring: settings.refactoring || "Workspace", + }; } public onDidChangeConfiguration({ settings }: DidChangeConfigurationParams) { if ("kdb" in settings) { - const kdb = settings.kdb; - this.setSettings({ - debug: kdb.debug_parser === true || false, - linting: kdb.linting === true || false, - }); + this.setSettings(settings.kdb); } } - public onDidClose({ document }: TextDocumentChangeEvent) { - this.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] }); + /* istanbul ignore next */ + public onDidChangeWatchedFiles({ changes }: DidChangeWatchedFilesParams) { + try { + this.parseFiles( + changes.reduce((matches, change) => { + if (change.type === FileChangeType.Deleted) { + this.cached.delete(change.uri); + } else { + matches.push(fileURLToPath(change.uri)); + } + return matches; + }, [] as string[]), + ); + } catch (error) { + this.connection.window.showErrorMessage(`${error}`); + } } public onDidChangeContent({ @@ -152,6 +202,10 @@ export default class QLangServer { } } + public onDidClose({ document }: TextDocumentChangeEvent) { + this.connection.sendDiagnostics({ uri: document.uri, diagnostics: [] }); + } + public onDocumentSymbol({ textDocument, }: DocumentSymbolParams): DocumentSymbol[] { @@ -171,9 +225,13 @@ export default class QLangServer { public onReferences({ textDocument, position }: ReferenceParams): Location[] { const tokens = this.parse(textDocument); const source = positionToToken(tokens, position); - return findIdentifiers(FindKind.Reference, tokens, source).map((token) => - Location.create(textDocument.uri, rangeFromToken(token)), - ); + return this.context({ uri: textDocument.uri, tokens }) + .map((document) => + findIdentifiers(FindKind.Reference, document.tokens, source).map( + (token) => Location.create(document.uri, rangeFromToken(token)), + ), + ) + .flat(); } public onDefinition({ @@ -182,34 +240,39 @@ export default class QLangServer { }: DefinitionParams): Location[] { const tokens = this.parse(textDocument); const source = positionToToken(tokens, position); - return findIdentifiers(FindKind.Definition, tokens, source).map((token) => - Location.create(textDocument.uri, rangeFromToken(token)), - ); + return this.context({ uri: textDocument.uri, tokens }) + .map((document) => + findIdentifiers(FindKind.Definition, document.tokens, source).map( + (token) => Location.create(document.uri, rangeFromToken(token)), + ), + ) + .flat(); } public onRenameRequest({ textDocument, position, newName, - }: RenameParams): WorkspaceEdit | null { + }: RenameParams): WorkspaceEdit { const tokens = this.parse(textDocument); const source = positionToToken(tokens, position); - const refs = findIdentifiers(FindKind.Rename, tokens, source); - if (refs.length === 0) { - return null; - } - const name = { - image: newName, - namespace: source?.namespace, - }; - const edits = refs.map((token) => { - return TextEdit.replace(rangeFromToken(token), relative(name, token)); - }); - return { - changes: { - [textDocument.uri]: edits, + const all = this.settings.refactoring === "Workspace"; + return this.context({ uri: textDocument.uri, tokens }, all).reduce( + (edit, document) => { + const refs = findIdentifiers(FindKind.Rename, document.tokens, source); + if (refs.length > 0) { + const name = { + image: newName, + namespace: source?.namespace, + }; + edit.changes![document.uri] = refs.map((token) => + TextEdit.replace(rangeFromToken(token), relative(name, token)), + ); + } + return edit; }, - }; + { changes: {} } as WorkspaceEdit, + ); } public onCompletion({ @@ -218,16 +281,22 @@ export default class QLangServer { }: CompletionParams): CompletionItem[] { const tokens = this.parse(textDocument); const source = positionToToken(tokens, position); - return findIdentifiers(FindKind.Completion, tokens, source).map((token) => { - return { - label: token.image, - labelDetails: { - detail: ` .${namespace(token)}`, - }, - kind: CompletionItemKind.Variable, - insertText: relative(token, source), - }; - }); + return this.context({ uri: textDocument.uri, tokens }) + .map((document) => + findIdentifiers(FindKind.Completion, document.tokens, source).map( + (token) => { + return { + label: token.image, + labelDetails: { + detail: ` .${namespace(token)}`, + }, + kind: CompletionItemKind.Variable, + insertText: relative(token, source), + }; + }, + ), + ) + .flat(); } public onExpressionRange({ @@ -300,6 +369,113 @@ export default class QLangServer { return ranges; } + public onPrepareCallHierarchy({ + textDocument, + position, + }: CallHierarchyPrepareParams): CallHierarchyItem[] { + const tokens = this.parse(textDocument); + const source = positionToToken(tokens, position); + if (source && assignable(source)) { + return [ + { + data: true, + kind: SymbolKind.Variable, + name: source.image, + uri: textDocument.uri, + range: rangeFromToken(source), + selectionRange: rangeFromToken(source), + }, + ]; + } + return []; + } + + public onIncomingCallsCallHierarchy({ + item, + }: CallHierarchyIncomingCallsParams): CallHierarchyIncomingCall[] { + const tokens = this.parse({ uri: item.uri }); + const source = positionToToken(tokens, item.range.end); + return item.data + ? this.context({ uri: item.uri, tokens }) + .map((document) => + findIdentifiers(FindKind.Reference, document.tokens, source) + .filter((token) => !assigned(token)) + .map((token) => { + const lambda = inLambda(token); + return { + from: { + kind: lambda ? SymbolKind.Object : SymbolKind.Function, + name: token.image, + uri: document.uri, + range: rangeFromToken(lambda || token), + selectionRange: rangeFromToken(token), + }, + fromRanges: [], + } as CallHierarchyIncomingCall; + }), + ) + .flat() + : []; + } + + public onOutgoingCallsCallHierarchy({ + item, + }: CallHierarchyOutgoingCallsParams): CallHierarchyOutgoingCall[] { + const tokens = this.parse({ uri: item.uri }); + const source = positionToToken(tokens, item.range.end); + return item.data + ? this.context({ uri: item.uri, tokens }) + .map((document) => + findIdentifiers(FindKind.Reference, document.tokens, source) + .filter((token) => inLambda(token) && !assigned(token)) + .map((token) => { + return { + to: { + kind: SymbolKind.Object, + name: token.image, + uri: document.uri, + range: rangeFromToken(inLambda(token)!), + selectionRange: rangeFromToken(token), + }, + fromRanges: [], + } as CallHierarchyOutgoingCall; + }), + ) + .flat() + : []; + } + + /* istanbul ignore next */ + public scan() { + const folders = this.params.workspaceFolders; + if (folders) { + try { + for (const folder of folders) { + this.parseFiles( + glob("**/*.{q,quke}", { + dot: true, + absolute: true, + nodir: true, + follow: false, + ignore: "node_modules/**/*.*", + cwd: fileURLToPath(folder.uri), + }), + ); + } + } catch (error) { + this.connection.window.showErrorMessage(`${error}`); + } + } + } + + /* istanbul ignore next */ + private parseFiles(matches: string[]) { + for (const match of matches) { + const file = readFileSync(match, "utf-8"); + this.cached.set(pathToFileURL(match).toString(), parse(file)); + } + } + private parse(textDocument: TextDocumentIdentifier): Token[] { const document = this.documents.get(textDocument.uri); if (!document) { @@ -307,6 +483,28 @@ export default class QLangServer { } return parse(document.getText()); } + + private context({ uri, tokens }: Tokenized, all = true): Tokenized[] { + if (all) { + this.documents.all().forEach((document) => { + const path = fileURLToPath(document.uri); + if (path) { + this.cached.set( + pathToFileURL(path).toString(), + document.uri === uri ? tokens : parse(document.getText()), + ); + } + }); + return Array.from(this.cached.entries(), (entry) => ({ + uri: entry[0], + tokens: entry[1], + })); + } + return this.documents.all().map((document) => ({ + uri: document.uri, + tokens: document.uri === uri ? tokens : parse(document.getText()), + })); + } } function rangeFromToken(token: Token): Range { diff --git a/server/src/server.ts b/server/src/server.ts index 32ed684f..9225fefa 100644 --- a/server/src/server.ts +++ b/server/src/server.ts @@ -35,13 +35,12 @@ connection.onInitialized(() => { section: "kdb", }); - if (connection.workspace) { - connection.workspace.getConfiguration("kdb").then((settings) => { - if (server) { - server.setSettings(settings); - } - }); - } + connection.workspace.getConfiguration("kdb").then((settings) => { + if (server) { + server.setSettings(settings); + server.scan(); + } + }); }); connection.listen(); diff --git a/src/classes/insightsConnection.ts b/src/classes/insightsConnection.ts index c927d9e0..85edd732 100644 --- a/src/classes/insightsConnection.ts +++ b/src/classes/insightsConnection.ts @@ -42,7 +42,7 @@ export class InsightsConnection { public node: InsightsNode; public meta?: MetaObject; public config?: InsightsConfig; - public insightsVersion?: string; + public insightsVersion?: number; public connEndpoints?: InsightsEndpoints; constructor(connLabel: string, node: InsightsNode) { @@ -145,50 +145,46 @@ export class InsightsConnection { const version = match ? match[0].replace(/-/g, "") : null; if (version) { const [major, minor, _path] = version.split("."); - this.insightsVersion = `${major}.${minor}`; + this.insightsVersion = parseFloat(`${major}.${minor}`); } } public defineEndpoints() { - if (this.insightsVersion) { - switch (this.insightsVersion) { - // uncomment it when SCRATCHPAD merge to Insights - // case "1.11": - // this.connEndpoints = { - // scratchpad: { - // scratchpad: "scratchpad-manager/api/v1/execute/display", - // import: "scratchpad-manager/api/v1/execute/import/data", - // importSql: "scratchpad-manager/api/v1/execute/import/sql", - // importQsql: "scratchpad-manager/api/v1/execute/import/qsql", - // reset: "scratchpad-manager/api/v1/execute/reset", - // }, - // serviceGateway: { - // meta: "servicegateway/meta", - // data: "servicegateway/data", - // sql: "servicegateway/qe/sql", - // qsql: "servicegateway/qe/qsql", - // }, - // }; - // break; - default: - this.connEndpoints = { - scratchpad: { - scratchpad: "servicebroker/scratchpad/display", - import: "servicebroker/scratchpad/import/data", - importSql: "servicebroker/scratchpad/import/sql", - importQsql: "servicebroker/scratchpad/import/qsql", - reset: "servicebroker/scratchpad/reset", - }, - serviceGateway: { - meta: "servicegateway/meta", - data: "servicegateway/data", - sql: "servicegateway/qe/sql", - qsql: "servicegateway/qe/qsql", - }, - }; - break; - } - } + this.connEndpoints = { + scratchpad: { + scratchpad: "servicebroker/scratchpad/display", + import: "servicebroker/scratchpad/import/data", + importSql: "servicebroker/scratchpad/import/sql", + importQsql: "servicebroker/scratchpad/import/qsql", + reset: "servicebroker/scratchpad/reset", + }, + serviceGateway: { + meta: "servicegateway/meta", + data: "servicegateway/data", + sql: "servicegateway/qe/sql", + qsql: "servicegateway/qe/qsql", + }, + }; + // uncomment this WHEN the insights version is available + // if (this.insightsVersion) { + // if (this.insightsVersion >= 1.12) { + // this.connEndpoints = { + // scratchpad: { + // scratchpad: "scratchpad/execute/display", + // import: "scratchpad/execute/import/data", + // importSql: "scratchpad/execute/import/sql", + // importQsql: "scratchpad/execute/import/qsql", + // reset: "scratchpad/reset", + // }, + // serviceGateway: { + // meta: "servicegateway/meta", + // data: "servicegateway/data", + // sql: "servicegateway/qe/sql", + // qsql: "servicegateway/qe/qsql", + // }, + // }; + // } + // } } public retrieveEndpoints( @@ -350,7 +346,7 @@ export class InsightsConnection { isTableView: false, params: queryParams, }; - window.withProgress( + await window.withProgress( { location: ProgressLocation.Notification, cancellable: false, @@ -362,33 +358,32 @@ export class InsightsConnection { progress.report({ message: "Populating scratchpad..." }); - const scratchpadResponse = await axios.post( - scratchpadURL.toString(), - body, - headers, - ); - - kdbOutputLog( - `Executed successfully, stored in ${variableName}.`, - "INFO", - ); - kdbOutputLog( - `[SCRATCHPAD] Status: ${scratchpadResponse.status}`, - "INFO", - ); - kdbOutputLog( - `[SCRATCHPAD] Populated scratchpad with the following params: ${JSON.stringify(body.params)}`, - "INFO", - ); - window.showInformationMessage( - `Executed successfully, stored in ${variableName}.`, - ); - Telemetry.sendEvent( - "Datasource." + dsTypeString + ".Scratchpad.Populated", - ); - - const p = new Promise((resolve) => resolve()); - return p; + return await axios + .post(scratchpadURL.toString(), body, headers) + .then((response: any) => { + if (response.data.error) { + kdbOutputLog( + `[SCRATCHPAD] Error occured while populating scratchpad: ${response.data.errorMsg}`, + "ERROR", + ); + } else { + kdbOutputLog( + `Executed successfully, stored in ${variableName}.`, + "INFO", + ); + kdbOutputLog(`[SCRATCHPAD] Status: ${response.status}`, "INFO"); + kdbOutputLog( + `[SCRATCHPAD] Populated scratchpad with the following params: ${JSON.stringify(body.params)}`, + "INFO", + ); + window.showInformationMessage( + `Executed successfully, stored in ${variableName}.`, + ); + Telemetry.sendEvent( + "Datasource." + dsTypeString + ".Scratchpad.Populated", + ); + } + }); }, ); } else { diff --git a/src/commands/dataSourceCommand.ts b/src/commands/dataSourceCommand.ts index f8461ebb..34643893 100644 --- a/src/commands/dataSourceCommand.ts +++ b/src/commands/dataSourceCommand.ts @@ -40,12 +40,12 @@ import { writeQueryResultsToConsole, writeQueryResultsToView, } from "./serverCommand"; -import { ServerType } from "../models/server"; import { Telemetry } from "../utils/telemetryClient"; import { LocalConnection } from "../classes/localConnection"; import { ConnectionManagementService } from "../services/connectionManagerService"; import { InsightsConnection } from "../classes/insightsConnection"; import { kdbOutputLog, offerConnectAction } from "../utils/core"; +import { ServerType } from "../models/connectionsModels"; export async function addDataSource(): Promise { const kdbDataSourcesFolderPath = createKdbDataSourcesFolder(); @@ -283,6 +283,13 @@ export function getApiBody( if (optional.temporal) { apiBody.temporality = api.temporality; } + if (optional.rowLimit && api.rowCountLimit) { + if (api.isRowLimitLast) { + apiBody.limit = -parseInt(api.rowCountLimit); + } else { + apiBody.limit = parseInt(api.rowCountLimit); + } + } const labels = optional.labels.filter((label) => label.active); @@ -291,6 +298,8 @@ export function getApiBody( {}, ...labels.map((label) => ({ [label.key]: label.value })), ); + } else { + apiBody.labels = {}; } const filters = optional.filters diff --git a/src/commands/installTools.ts b/src/commands/installTools.ts index 799be259..a7befef7 100644 --- a/src/commands/installTools.ts +++ b/src/commands/installTools.ts @@ -43,7 +43,6 @@ import { onboardingInput, onboardingWorkflow, } from "../models/items/onboarding"; -import { Server } from "../models/server"; import { KdbNode } from "../services/kdbTreeProvider"; import { addLocalConnectionContexts, @@ -64,6 +63,7 @@ import { executeCommand } from "../utils/cpUtils"; import { openUrl } from "../utils/openUrl"; import { Telemetry } from "../utils/telemetryClient"; import { validateServerPort } from "../validators/kdbValidator"; +import { Server } from "../models/connectionsModels"; export async function installTools(): Promise { let file: Uri[] | undefined; diff --git a/src/commands/serverCommand.ts b/src/commands/serverCommand.ts index 0837fcca..3faba8dc 100644 --- a/src/commands/serverCommand.ts +++ b/src/commands/serverCommand.ts @@ -26,10 +26,8 @@ import { import { ext } from "../extensionVariables"; import { DataSourceFiles } from "../models/dataSource"; import { ExecutionTypes } from "../models/execution"; -import { InsightDetails, Insights } from "../models/insights"; import { queryConstants } from "../models/queryResult"; import { ScratchpadResult } from "../models/scratchpadResult"; -import { Server, ServerDetails, ServerType } from "../models/server"; import { ServerObject } from "../models/serverObject"; import { DataSourcesPanel } from "../panels/datasource"; import { @@ -69,6 +67,15 @@ import { ConnectionManagementService } from "../services/connectionManagerServic import { InsightsConnection } from "../classes/insightsConnection"; import { MetaContentProvider } from "../services/metaContentProvider"; import { handleLabelsConnMap, removeConnFromLabels } from "../utils/connLabel"; +import { + ExportedConnections, + InsightDetails, + Insights, + Server, + ServerDetails, + ServerType, +} from "../models/connectionsModels"; +import * as fs from "fs"; export async function addNewConnection(): Promise { NewConnectionPannel.close(); @@ -370,7 +377,7 @@ export async function addKdbConnection( tls: kdbData.tls, }, }; - if (servers[0].managed) { + if (servers.key.managed) { await addLocalConnectionContexts(getServerName(servers[0])); } } else { @@ -533,6 +540,154 @@ export async function editKdbConnection( } } +// test fs readFileSync unit tests are flaky, no correct way to test them +/* istanbul ignore next */ +export async function importConnections() { + const options = { + canSelectMany: false, + openLabel: "Select JSON File", + filters: { + "JSON Files": ["json"], + "All Files": ["*"], + }, + }; + + const fileUri = await window.showOpenDialog(options); + if (!fileUri || fileUri.length === 0) { + kdbOutputLog("[IMPORT CONNECTION]No file selected", "ERROR"); + return; + } + const filePath = fileUri[0].fsPath; + const fileContent = fs.readFileSync(filePath, "utf8"); + + let importedConnections: ExportedConnections; + try { + importedConnections = JSON.parse(fileContent); + } catch (e) { + kdbOutputLog("[IMPORT CONNECTION]Invalid JSON format", "ERROR"); + return; + } + + if (!isValidExportedConnections(importedConnections)) { + kdbOutputLog( + "[IMPORT CONNECTION]JSON does not match the required format", + "ERROR", + ); + return; + } + if ( + importedConnections.connections.KDB.length === 0 && + importedConnections.connections.Insights.length === 0 + ) { + kdbOutputLog( + "[IMPORT CONNECTION]There is no KDB or Insights connections to import in this JSON file", + "ERROR", + ); + return; + } + await addImportedConnections(importedConnections); +} + +export async function addImportedConnections( + importedConnections: ExportedConnections, +) { + const connMangService = new ConnectionManagementService(); + const existingAliases = connMangService.retrieveListOfConnectionsNames(); + const localAlreadyExists = existingAliases.has("local"); + + const hasDuplicates = + importedConnections.connections.Insights.some((connection) => + existingAliases.has( + connMangService.checkConnAlias(connection.alias, true), + ), + ) || + importedConnections.connections.KDB.some((connection) => + existingAliases.has( + connMangService.checkConnAlias( + connection.serverAlias, + false, + localAlreadyExists, + ), + ), + ); + + let res: "Duplicate" | "Overwrite" | "Cancel" | undefined = "Duplicate"; + if (hasDuplicates) { + res = await window.showInformationMessage( + "You are importing connections with the same name. Would you like to duplicate, overwrite or cancel the import?", + "Duplicate", + "Overwrite", + "Cancel", + ); + if (!res || res === "Cancel") { + return; + } + } + + let counter = 1; + const insights: InsightDetails[] = []; + for (const connection of importedConnections.connections.Insights) { + let alias = connMangService.checkConnAlias(connection.alias, true); + + if (res === "Overwrite") { + insights.push(connection); + } else { + while (existingAliases.has(alias)) { + alias = `${connection.alias}-${counter}`; + counter++; + } + connection.alias = alias; + await addInsightsConnection(connection); + } + existingAliases.add(alias); + counter = 1; + } + + const servers: ServerDetails[] = []; + for (const connection of importedConnections.connections.KDB) { + let alias = connMangService.checkConnAlias( + connection.serverAlias, + false, + localAlreadyExists, + ); + + if (res === "Overwrite") { + servers.push(connection); + } else { + while (existingAliases.has(alias)) { + alias = `${connection.serverAlias}-${counter}`; + counter++; + } + connection.serverAlias = alias; + const isManaged = alias === "local"; + await addKdbConnection(connection, isManaged); + } + existingAliases.add(alias); + counter = 1; + } + + if (insights.length > 0) { + const config = insights.reduce((config, connection) => { + config[connection.alias] = connection; + return config; + }, getInsights() || {}); + await updateInsights(config); + ext.serverProvider.refreshInsights(config); + } + + if (servers.length > 0) { + const config = servers.reduce((config, connection) => { + config[connection.serverAlias] = connection; + return config; + }, getServers() || {}); + await updateServers(config); + ext.serverProvider.refresh(config); + } + + kdbOutputLog("[IMPORT CONNECTION]Connections imported successfully", "INFO"); + window.showInformationMessage("Connections imported successfully"); +} + export async function removeConnection(viewItem: KdbNode | InsightsNode) { const connMngService = new ConnectionManagementService(); removeConnFromLabels( @@ -545,7 +700,6 @@ export async function removeConnection(viewItem: KdbNode | InsightsNode) { export async function connect(connLabel: string): Promise { const connMngService = new ConnectionManagementService(); - commands.executeCommand("kdb-results.focus"); ExecutionConsole.start(); const viewItem = connMngService.retrieveConnection(connLabel); if (viewItem === undefined) { @@ -623,14 +777,12 @@ export async function executeQuery( context: string, isPython: boolean, isWorkbook: boolean, + isFromConnTree?: boolean, ): Promise { const connMngService = new ConnectionManagementService(); const queryConsole = ExecutionConsole.start(); if (connLabel === "") { if (ext.activeConnection === undefined) { - window.showErrorMessage( - "No active connection found. Connect to one connection.", - ); kdbOutputLog( "No active connection found. Connect to one connection.", "ERROR", @@ -660,6 +812,8 @@ export async function executeQuery( isWorkbook ? "WORKBOOK" : "SCRATCHPAD", isPython, false, + undefined, + isFromConnTree, ); return undefined; } @@ -698,6 +852,7 @@ export async function executeQuery( isWorkbook ? "WORKBOOK" : "SCRATCHPAD", isPython, duration, + isFromConnTree, ); } else { writeQueryResultsToConsole( @@ -709,6 +864,7 @@ export async function executeQuery( isWorkbook ? "WORKBOOK" : "SCRATCHPAD", isPython, duration, + isFromConnTree, ); } } @@ -817,6 +973,7 @@ export function rerunQuery(rerunQueryElement: QueryHistory) { context, rerunQueryElement.language !== "q", !!rerunQueryElement.isWorkbook, + !!rerunQueryElement.isFromConnTree, ); } else { const dsFile = rerunQueryElement.query as DataSourceFiles; @@ -876,6 +1033,63 @@ export async function openMeta(node: MetaObjectPayloadNode | InsightsMetaNode) { } } +export async function exportConnections(connLabel?: string) { + const connMngService = new ConnectionManagementService(); + + const exportAuth = await window.showQuickPick(["Yes", "No"], { + placeHolder: "Do you want to export username and password?", + }); + + if (!exportAuth) { + kdbOutputLog( + "[EXPORT CONNECTIONS] Export operation was cancelled by the user", + "INFO", + ); + return; + } + + const includeAuth = exportAuth === "Yes"; + if (includeAuth) { + await connMngService.retrieveUserPass(); + } + const doc = await connMngService.exportConnection(connLabel, includeAuth); + if (doc && doc !== "") { + const formattedDoc = JSON.stringify(JSON.parse(doc), null, 2); + const uri = await window.showSaveDialog({ + saveLabel: "Save Exported Connections", + filters: { + "JSON Files": ["json"], + "All Files": ["*"], + }, + }); + if (uri) { + fs.writeFile(uri.fsPath, formattedDoc, (err) => { + if (err) { + kdbOutputLog( + `[EXPORT CONNECTIONS] Error saving file: ${err.message}`, + "ERROR", + ); + window.showErrorMessage(`Error saving file: ${err.message}`); + } else { + workspace.openTextDocument(uri).then((document) => { + window.showTextDocument(document, { preview: false }); + }); + } + }); + } else { + kdbOutputLog( + "[EXPORT CONNECTIONS] Save operation was cancelled by the user", + "INFO", + ); + } + } else { + kdbOutputLog( + "[EXPORT CONNECTIONS] No connections found to be exported", + "ERROR", + ); + } +} + export function writeQueryResultsToConsole( result: string | string[], query: string, @@ -885,6 +1099,7 @@ export function writeQueryResultsToConsole( type?: string, isPython?: boolean, duration?: string, + isFromConnTree?: boolean, ): void { const queryConsole = ExecutionConsole.start(); const isNonEmptyArray = Array.isArray(result) && result.length > 0; @@ -900,6 +1115,7 @@ export function writeQueryResultsToConsole( type, isPython, duration, + isFromConnTree, ); } else { if (!checkIfIsDatasource(type)) { @@ -914,6 +1130,7 @@ export function writeQueryResultsToConsole( isPython, false, duration, + isFromConnTree, ); } } @@ -928,8 +1145,9 @@ export function writeQueryResultsToView( type?: string, isPython?: boolean, duration?: string, + isFromConnTree?: boolean, ): void { - commands.executeCommand("kdb.resultsPanel.update", result, isInsights, type); + commands.executeCommand("kdb.resultsPanel.update", result, isInsights); if (!checkIfIsDatasource(type)) { addQueryHistory( query, @@ -942,6 +1160,7 @@ export function writeQueryResultsToView( undefined, undefined, duration, + isFromConnTree, ); } } @@ -996,3 +1215,12 @@ export function writeScratchpadResult( } } } + +function isValidExportedConnections(data: any): data is ExportedConnections { + return ( + data && + data.connections && + Array.isArray(data.connections.Insights) && + Array.isArray(data.connections.KDB) + ); +} diff --git a/src/extension.ts b/src/extension.ts index 0a1688a2..05c51176 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -51,17 +51,19 @@ import { editInsightsConnection, editKdbConnection, enableTLS, + executeQuery, + exportConnections, + importConnections, openMeta, refreshGetMeta, removeConnection, rerunQuery, + resetScratchPad, } from "./commands/serverCommand"; import { showInstallationDetails } from "./commands/walkthroughCommand"; import { ext } from "./extensionVariables"; import { ExecutionTypes } from "./models/execution"; -import { InsightDetails, Insights } from "./models/insights"; import { QueryResult } from "./models/queryResult"; -import { Server, ServerDetails } from "./models/server"; import { InsightsMetaNode, InsightsNode, @@ -114,6 +116,12 @@ import { setLabelColor, } from "./utils/connLabel"; import { activateTextDocument } from "./utils/workspace"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "./models/connectionsModels"; let client: LanguageClient; @@ -177,11 +185,13 @@ export async function activate(context: ExtensionContext) { AuthSettings.init(context); ext.secretSettings = AuthSettings.instance; + commands.executeCommand("kdb-results.focus"); + kdbOutputLog("kdb extension is now active!", "INFO"); try { // check for installed q runtime - await checkLocalInstall(); + await checkLocalInstall(true); } catch (err) { window.showErrorMessage(`${err}`); } @@ -194,12 +204,8 @@ export async function activate(context: ExtensionContext) { ), commands.registerCommand( "kdb.resultsPanel.update", - (results: string, isInsights: boolean, dataSourceType?: string) => { - ext.resultsViewProvider.updateResults( - results, - isInsights, - dataSourceType, - ); + (results: string, isInsights: boolean) => { + ext.resultsViewProvider.updateResults(results, isInsights); }, ), commands.registerCommand("kdb.resultsPanel.clear", () => { @@ -265,6 +271,40 @@ export async function activate(context: ExtensionContext) { await disconnect(connLabel); }, ), + commands.registerCommand("kdb.connections.export.all", () => { + exportConnections(); + }), + commands.registerCommand( + "kdb.connections.export.single", + async (viewItem: KdbNode | InsightsNode) => { + exportConnections(viewItem.label); + }, + ), + commands.registerCommand("kdb.connections.import", async () => { + await importConnections(); + }), + commands.registerCommand( + "kdb.connection.content.selectView", + async (viewItem) => { + const connLabel = viewItem.connLabel + ? viewItem.connLabel.split("[")[1].split("]")[0] + : undefined; + if (connLabel) { + const executorName = viewItem.coreIcon.substring(2); + executeQuery( + viewItem.label, + connLabel, + executorName, + "", + false, + false, + true, + ); + } else { + kdbOutputLog("Connection label not found", "ERROR"); + } + }, + ), commands.registerCommand( "kdb.open.meta", async (viewItem: InsightsMetaNode | MetaObjectPayloadNode) => { diff --git a/src/extensionVariables.ts b/src/extensionVariables.ts index bac7d95e..7b894df2 100644 --- a/src/extensionVariables.ts +++ b/src/extensionVariables.ts @@ -38,6 +38,7 @@ import { LocalConnection } from "./classes/localConnection"; import { InsightsConnection } from "./classes/insightsConnection"; import { DataSourceFiles } from "./models/dataSource"; import { ConnectionLabel, LabelColors, Labels } from "./models/labels"; +import { kdbAuthMap } from "./models/connectionsModels"; // eslint-disable-next-line @typescript-eslint/no-namespace export namespace ext { @@ -80,8 +81,10 @@ export namespace ext { export const kdbDataSourceRootNodes: string[] = []; export const kdbQueryHistoryNodes: string[] = []; export const kdbQueryHistoryList: QueryHistory[] = []; + export const kdbAuthMap: kdbAuthMap[] = []; export const kdbrootNodes: string[] = []; export const kdbinsightsNodes: string[] = []; + export const selectContentNodesContext: string[] = ["kdbVariable"]; export const kdbNodesWithoutAuth: string[] = []; export const kdbNodesWithoutTls: string[] = []; export const kdbConnectionAliasList: string[] = []; @@ -181,7 +184,7 @@ export namespace ext { "Tables", "Variables", "Views", - "Namespaces", + // "Namespaces", removed to investigate ]; export const qNamespaceFilters = [".q", ".Q", ".h", ".z", ".o", ".j", ".m"]; diff --git a/src/models/server.ts b/src/models/connectionsModels.ts similarity index 65% rename from src/models/server.ts rename to src/models/connectionsModels.ts index af400b55..d200d5b8 100644 --- a/src/models/server.ts +++ b/src/models/connectionsModels.ts @@ -11,6 +11,8 @@ * specific language governing permissions and limitations under the License. */ +//TODO: start to migrate all connections models to here + export enum ServerType { INSIGHTS, KDB, @@ -31,3 +33,29 @@ export interface ServerDetails { export interface Server { [name: string]: ServerDetails; } + +export interface kdbAuthMap { + [name: string]: { + username: string; + password: string; + }; +} + +export interface InsightDetails { + alias: string; + server: string; + auth: boolean; + realm?: string; + insecure?: boolean; +} + +export interface Insights { + [name: string]: InsightDetails; +} + +export interface ExportedConnections { + connections: { + Insights: InsightDetails[]; + KDB: ServerDetails[]; + }; +} diff --git a/src/models/data.ts b/src/models/data.ts index 57b7034b..ca4366a6 100644 --- a/src/models/data.ts +++ b/src/models/data.ts @@ -37,4 +37,5 @@ export type getDataBodyPayload = { temporality?: string; slice?: string[]; sortCols?: string[]; + limit?: number; }; diff --git a/src/models/dataSource.ts b/src/models/dataSource.ts index a74928a7..5a2e96e9 100644 --- a/src/models/dataSource.ts +++ b/src/models/dataSource.ts @@ -17,6 +17,7 @@ export enum DataSourceTypes { SQL = "SQL", } +//TODO: make the optional params required in 1.10 or superior export interface DataSourceFiles { name?: string; originalName?: string; @@ -30,6 +31,8 @@ export interface DataSourceFiles { endTS: string; fill: string; temporality: string; + rowCountLimit?: string; + isRowLimitLast?: boolean; filter: string[]; groupBy: string[]; agg: string[]; @@ -44,6 +47,7 @@ export interface DataSourceFiles { sorts: Sort[]; aggs: Agg[]; groups: Group[]; + rowLimit?: boolean; }; }; qsql: { @@ -68,6 +72,8 @@ export function createDefaultDataSourceFile(): DataSourceFiles { endTS: "", fill: "zero", temporality: "snapshot", + rowCountLimit: "100000", + isRowLimitLast: true, filter: [], groupBy: [], agg: [], diff --git a/src/models/insights.ts b/src/models/insights.ts deleted file mode 100644 index b9446e4d..00000000 --- a/src/models/insights.ts +++ /dev/null @@ -1,24 +0,0 @@ -/* - * Copyright (c) 1998-2023 Kx Systems Inc. - * - * Licensed under the Apache License, Version 2.0 (the "License"); you may not use this file except in compliance with the - * License. You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software distributed under the License is distributed on an - * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the License for the - * specific language governing permissions and limitations under the License. - */ - -export interface InsightDetails { - alias: string; - server: string; - auth: boolean; - realm?: string; - insecure?: boolean; -} - -export interface Insights { - [name: string]: InsightDetails; -} diff --git a/src/models/messages.ts b/src/models/messages.ts index 8b51000a..222ebe4f 100644 --- a/src/models/messages.ts +++ b/src/models/messages.ts @@ -38,6 +38,7 @@ export interface DataSourceMessage2 { command: DataSourceCommand; servers: string[]; selectedServer: string; + selectedServerVersion: number; isInsights: boolean; insightsMeta: MetaObjectPayload; dataSourceFile: DataSourceFiles; diff --git a/src/models/queryHistory.ts b/src/models/queryHistory.ts index 1fcb9be7..eeff04a8 100644 --- a/src/models/queryHistory.ts +++ b/src/models/queryHistory.ts @@ -11,8 +11,8 @@ * specific language governing permissions and limitations under the License. */ +import { ServerType } from "./connectionsModels"; import { DataSourceFiles, DataSourceTypes } from "./dataSource"; -import { ServerType } from "./server"; export interface QueryHistory { executorName: string; @@ -26,4 +26,5 @@ export interface QueryHistory { datasourceType?: DataSourceTypes; isWorkbook?: boolean; duration?: string; + isFromConnTree?: boolean; } diff --git a/src/services/connectionManagerService.ts b/src/services/connectionManagerService.ts index 1a509bad..3f439d8a 100644 --- a/src/services/connectionManagerService.ts +++ b/src/services/connectionManagerService.ts @@ -28,11 +28,15 @@ import { updateInsights, updateServers, } from "../utils/core"; -import { Insights } from "../models/insights"; -import { Server } from "../models/server"; import { refreshDataSourcesPanel } from "../utils/dataSource"; import { MetaInfoType } from "../models/meta"; import { retrieveConnLabelsNames } from "../utils/connLabel"; +import { + ExportedConnections, + Insights, + kdbAuthMap, + Server, +} from "../models/connectionsModels"; export class ConnectionManagementService { public retrieveConnection( @@ -65,6 +69,30 @@ export class ConnectionManagementService { ); } + public retrieveListOfConnectionsNames(): Set { + return new Set( + ext.connectionsList.map((conn) => { + if (conn instanceof KdbNode) { + return conn.details.serverAlias; + } else { + return conn.details.alias; + } + }), + ); + } + + public checkConnAlias( + alias: string, + isInsights: boolean, + localAlreadyExists?: boolean, + ): string { + if (isInsights) { + return alias !== "local" ? alias : "localInsights"; + } else { + return localAlreadyExists && alias === "local" ? alias + "KDB" : alias; + } + } + public isKdbConnection(connection: KdbNode | InsightsNode): boolean { return connection instanceof KdbNode; } @@ -312,41 +340,35 @@ export class ConnectionManagementService { } public async resetScratchpad(): Promise { - let error = true; - if (!ext.activeConnection) { - window.showErrorMessage( - "Please active an Insights connection to use this feature.", + if ( + !ext.activeConnection || + !(ext.activeConnection instanceof InsightsConnection) + ) { + kdbOutputLog( + "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", + "ERROR", ); return; } - const confirmationPromt = { - prompt: - "Are you sure you want to reset the scratchpad from the connecttion " + - ext.activeConnection.connLabel + - "?", - option1: "Yes", - option2: "No", - }; - await window - .showInformationMessage( - confirmationPromt.prompt, - confirmationPromt.option1, - confirmationPromt.option2, - ) - .then(async (selection) => { - if (selection === confirmationPromt.option1) { - if (ext.activeConnection instanceof InsightsConnection) { - error = false; - return await ext.activeConnection.resetScratchpad(); - } else { - return; - } - } - }); - if (error) { - window.showErrorMessage( - "This feature is only available for Insights connections.", + if ( + ext.activeConnection.insightsVersion && + ext.activeConnection.insightsVersion >= 1.12 + ) { + const confirmationPrompt = `Are you sure you want to reset the scratchpad from the connection ${ext.activeConnection.connLabel}?`; + const selection = await window.showInformationMessage( + confirmationPrompt, + "Yes", + "No", + ); + + if (selection === "Yes") { + await ext.activeConnection.resetScratchpad(); + } + } else { + kdbOutputLog( + "[RESET SCRATCHPAD] Please connect to an Insights connection with version superior to 1.11", + "ERROR", ); } } @@ -403,4 +425,84 @@ export class ConnectionManagementService { return connection.returnMetaObject(metaType); } + + public async retrieveUserPass() { + for (const connection of ext.connectionsList) { + if (connection instanceof KdbNode) { + const authCredentials = await ext.secretSettings.getAuthData( + connection.children[0], + ); + if (authCredentials) { + const [username, password] = authCredentials.split(":"); + const authMap: kdbAuthMap = { + [connection.children[0]]: { + username, + password, + }, + }; + ext.kdbAuthMap.push(authMap); + } + } + } + } + + public async retrieveInsightsConnVersion(connLabel: string): Promise { + const connection = this.retrieveConnectedConnection(connLabel); + if (!connection || !(connection instanceof InsightsConnection)) { + return 0; + } + return connection.insightsVersion ? connection.insightsVersion : 0; + } + + public exportConnection(connLabel?: string, includeAuth?: boolean): string { + const exportedContent: ExportedConnections = { + connections: { + Insights: [], + KDB: [], + }, + }; + if (connLabel) { + const connection = this.retrieveConnection(connLabel); + if (!connection) { + return ""; + } + if (connection instanceof KdbNode) { + exportedContent.connections.KDB.push(connection.details); + } else { + exportedContent.connections.Insights.push(connection.details); + } + } else { + ext.connectionsList.forEach((connection) => { + if (connection instanceof KdbNode) { + exportedContent.connections.KDB.push(connection.details); + } else { + exportedContent.connections.Insights.push(connection.details); + } + }); + } + + if (exportedContent.connections.KDB.length > 0) { + for (const kdbConn of exportedContent.connections.KDB) { + if (!includeAuth) { + kdbConn.auth = false; + kdbConn.username = undefined; + kdbConn.password = undefined; + } else { + const auth = ext.kdbAuthMap.find((auth) => { + return Object.keys(auth)[0] === kdbConn.serverAlias; + }); + if (auth) { + kdbConn.auth = true; + kdbConn.username = auth[kdbConn.serverAlias].username; + kdbConn.password = auth[kdbConn.serverAlias].password; + } + } + } + } + ext.kdbAuthMap.length = 0; + return exportedContent.connections.Insights.length === 0 && + exportedContent.connections.KDB.length === 0 + ? "" + : JSON.stringify(exportedContent, null, 2); + } } diff --git a/src/services/dataSourceEditorProvider.ts b/src/services/dataSourceEditorProvider.ts index 2fed725c..ab5a7c93 100644 --- a/src/services/dataSourceEditorProvider.ts +++ b/src/services/dataSourceEditorProvider.ts @@ -106,15 +106,19 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { webview.options = { enableScripts: true }; webview.html = this.getWebviewContent(webview); let changing = 0; + const connMngService = new ConnectionManagementService(); const updateWebview = async () => { if (changing === 0) { const selectedServer = getServerForUri(document.uri) || ""; + const selectedServerVersion = + await connMngService.retrieveInsightsConnVersion(selectedServer); await getConnectionForServer(selectedServer); webview.postMessage({ command: DataSourceCommand.Update, selectedServer, servers: getInsightsServers(), + selectedServerVersion, dataSourceFile: this.getDocumentAsJson(document), insightsMeta: await this.getMeta(selectedServer), isInsights: true, @@ -173,7 +177,6 @@ export class DataSourceEditorProvider implements CustomTextEditorProvider { ); break; case DataSourceCommand.Refresh: - const connMngService = new ConnectionManagementService(); const selectedServer = getServerForUri(document.uri) || ""; if (!connMngService.isConnected(selectedServer)) { offerConnectAction(selectedServer); diff --git a/src/services/kdbTreeProvider.ts b/src/services/kdbTreeProvider.ts index 55b20d2a..465d867a 100644 --- a/src/services/kdbTreeProvider.ts +++ b/src/services/kdbTreeProvider.ts @@ -22,8 +22,6 @@ import { commands, } from "vscode"; import { ext } from "../extensionVariables"; -import { InsightDetails, Insights } from "../models/insights"; -import { Server, ServerDetails } from "../models/server"; import { loadDictionaries, loadFunctions, @@ -49,6 +47,12 @@ import { retrieveConnLabelsNames, } from "../utils/connLabel"; import { Labels } from "../models/labels"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "../models/connectionsModels"; export class KdbTreeProvider implements TreeDataProvider { private _onDidChangeTreeData: EventEmitter< @@ -70,6 +74,11 @@ export class KdbTreeProvider implements TreeDataProvider { refresh(serverList: Server): void { ext.isBundleQCreated = false; this.serverList = serverList; + commands.executeCommand( + "setContext", + "kdb.selectContentNodesContext", + ext.selectContentNodesContext, + ); this._onDidChangeTreeData.fire(); } @@ -96,6 +105,9 @@ export class KdbTreeProvider implements TreeDataProvider { arguments: [element], }; } + if (element instanceof QServerNode) { + element.contextValue = ext.selectContentNodesContext[0]; + } return element; } @@ -136,7 +148,7 @@ export class KdbTreeProvider implements TreeDataProvider { element.contextValue !== undefined && ext.kdbrootNodes.indexOf(element.contextValue) !== -1 ) { - return Promise.resolve(await this.getNamespaces()); + return Promise.resolve(await this.getNamespaces(element.contextValue)); } else if ( element.contextValue !== undefined && ext.kdbinsightsNodes.indexOf(element.contextValue) !== -1 @@ -147,6 +159,7 @@ export class KdbTreeProvider implements TreeDataProvider { this.getCategories( (element as QNamespaceNode).details?.toString(), ext.qObjectCategories, + (element as QNamespaceNode).connLabel, ), ); } else if ( @@ -199,7 +212,8 @@ export class KdbTreeProvider implements TreeDataProvider { } } - private async getNamespaces(): Promise { + /* istanbul ignore next */ + private async getNamespaces(connLabel?: string): Promise { const ns = await loadNamespaces(); const result = ns.map( (x) => @@ -209,6 +223,7 @@ export class KdbTreeProvider implements TreeDataProvider { "", TreeItemCollapsibleState.Collapsed, x.fname, + connLabel ?? "", ), ); if (result !== undefined) { @@ -218,9 +233,11 @@ export class KdbTreeProvider implements TreeDataProvider { } } + /* istanbul ignore next */ private async getCategories( ns: string | undefined, objectCategories: string[], + connLabel?: string, ): Promise { // filter out views for non-default namespaces let filteredCategories; @@ -239,16 +256,20 @@ export class KdbTreeProvider implements TreeDataProvider { "", ns ?? "", TreeItemCollapsibleState.Collapsed, + connLabel ?? "", ), ); return result; } + /* istanbul ignore next */ private async getServerObjects( - serverType: TreeItem, + serverType: QCategoryNode | TreeItem, ): Promise { if (serverType === undefined) return new Array(); const ns = serverType.contextValue ?? ""; + const connLabel = + serverType instanceof QCategoryNode ? serverType.connLabel : ""; if (serverType.label === ext.qObjectCategories[0]) { // dictionaries const dicts = await loadDictionaries(serverType.contextValue ?? ""); @@ -259,7 +280,8 @@ export class KdbTreeProvider implements TreeDataProvider { `${ns === "." ? "" : ns + "."}${x.name}`, "", TreeItemCollapsibleState.None, - "p-dictionary", + "dictionaries", + connLabel, ), ); if (result !== undefined) { @@ -277,7 +299,8 @@ export class KdbTreeProvider implements TreeDataProvider { `${ns === "." ? "" : ns + "."}${x.name}`, "", TreeItemCollapsibleState.None, - "p-function", + "functions", + connLabel, ), ); if (result !== undefined) { @@ -295,7 +318,8 @@ export class KdbTreeProvider implements TreeDataProvider { `${ns === "." ? "" : ns + "."}${x.name}`, "", TreeItemCollapsibleState.None, - "p-table", + "tables", + connLabel, ), ); if (result !== undefined) { @@ -310,10 +334,11 @@ export class KdbTreeProvider implements TreeDataProvider { (x) => new QServerNode( [], - `${ns === "." ? "." : ""}${x.name}`, + `${ns === "." ? "" : ns + "."}${x.name}`, "", TreeItemCollapsibleState.None, - "p-var", + "variables", + connLabel, ), ); if (result !== undefined) { @@ -331,7 +356,8 @@ export class KdbTreeProvider implements TreeDataProvider { `${ns === "." ? "" : "."}${x}`, "", TreeItemCollapsibleState.None, - "p-view", + "views", + connLabel, ), ); if (result !== undefined) { @@ -339,25 +365,28 @@ export class KdbTreeProvider implements TreeDataProvider { } else { return new Array(); } - } else if (serverType.label === ext.qObjectCategories[5]) { - // nested namespaces - const namespaces = await loadNamespaces(ns); - const result = namespaces.map( - (x) => - new QNamespaceNode( - [], - x.fname, - "", - TreeItemCollapsibleState.Collapsed, - x.fname, - ), - ); - if (result !== undefined) { - return result; - } else { - return Array(); - } } + // Remove this for this moment, to investigate + // else if (serverType.label === ext.qObjectCategories[5]) { + // // nested namespaces + // const namespaces = await loadNamespaces(ns); + // const result = namespaces.map( + // (x) => + // new QNamespaceNode( + // [], + // x.fname, + // "", + // TreeItemCollapsibleState.Collapsed, + // x.fname, + // connLabel, + // ), + // ); + // if (result !== undefined) { + // return result; + // } else { + // return Array(); + // } + // } return new Array(); } @@ -558,7 +587,7 @@ export class KdbNode extends TreeItem { : ""; } - iconPath = getNamedIconPath("p-q-connection", this.label); + iconPath = getNamedIconPath("conn-kdb", this.label); contextValue = this.label; // "root"; } @@ -606,7 +635,7 @@ export class InsightsNode extends TreeItem { : ""; } - iconPath = getNamedIconPath("p-insights", this.label); + iconPath = getNamedIconPath("conn-insights", this.label); contextValue = this.label; // "root"; } @@ -627,24 +656,7 @@ export class InsightsMetaNode extends TreeItem { return ""; } - iconPath = { - light: path.join( - __filename, - "..", - "..", - "resources", - "light", - "metaicon.svg", - ), - dark: path.join( - __filename, - "..", - "..", - "resources", - "dark", - "metaicon.svg", - ), - }; + iconPath = getOtherIconPath("metaicon"); contextValue = "meta"; } @@ -655,6 +667,7 @@ export class QNamespaceNode extends TreeItem { public readonly details: string, public readonly collapsibleState: TreeItemCollapsibleState, public readonly fullName: string, + public readonly connLabel: string, ) { details = fullName; super(label, collapsibleState); @@ -665,24 +678,7 @@ export class QNamespaceNode extends TreeItem { return ""; } - iconPath = { - light: path.join( - __filename, - "..", - "..", - "resources", - "light", - "namespaces.svg", - ), - dark: path.join( - __filename, - "..", - "..", - "resources", - "dark", - "namespaces.svg", - ), - }; + iconPath = getOtherIconPath("namespaces"); contextValue = "ns"; } @@ -693,6 +689,7 @@ export class QCategoryNode extends TreeItem { public readonly details: string, public readonly ns: string, public readonly collapsibleState: TreeItemCollapsibleState, + public readonly connLabel: string, ) { details = ""; super(label, collapsibleState); @@ -703,24 +700,7 @@ export class QCategoryNode extends TreeItem { return ""; } - iconPath = { - light: path.join( - __filename, - "..", - "..", - "resources", - "light", - `${this.label.toLowerCase()}.svg`, - ), - dark: path.join( - __filename, - "..", - "..", - "resources", - "dark", - `${this.label.toLowerCase()}.svg`, - ), - }; + iconPath = getOtherIconPath(this.label.toLowerCase()); contextValue = this.ns; // "category"; } @@ -736,24 +716,7 @@ export class MetaObjectPayloadNode extends TreeItem { super(label, collapsibleState); this.description = ""; } - iconPath = { - light: path.join( - __filename, - "..", - "..", - "resources", - "light", - `${this.coreIcon}.svg`, - ), - dark: path.join( - __filename, - "..", - "..", - "resources", - "dark", - `${this.coreIcon}.svg`, - ), - }; + iconPath = getOtherIconPath(this.coreIcon); } export class QServerNode extends TreeItem { @@ -763,6 +726,7 @@ export class QServerNode extends TreeItem { public readonly details: string, public readonly collapsibleState: TreeItemCollapsibleState, public readonly coreIcon: string, + public readonly connLabel: string, ) { details = ""; super(label, collapsibleState); @@ -773,24 +737,7 @@ export class QServerNode extends TreeItem { return ""; } - iconPath = { - light: path.join( - __filename, - "..", - "..", - "resources", - "light", - `${this.coreIcon}.svg`, - ), - dark: path.join( - __filename, - "..", - "..", - "resources", - "dark", - `${this.coreIcon}.svg`, - ), - }; + iconPath = getOtherIconPath(this.coreIcon); contextValue = this.label; } @@ -857,3 +804,17 @@ function getNamedIconPath(name: string, label: string) { ), }; } + +function getOtherIconPath(name: string) { + return { + light: path.join( + __filename, + "..", + "..", + "resources", + "light", + name + ".svg", + ), + dark: path.join(__filename, "..", "..", "resources", "dark", name + ".svg"), + }; +} diff --git a/src/services/queryHistoryProvider.ts b/src/services/queryHistoryProvider.ts index 35ff9fe8..05957ba1 100644 --- a/src/services/queryHistoryProvider.ts +++ b/src/services/queryHistoryProvider.ts @@ -109,13 +109,35 @@ export class QueryHistoryTreeItem extends TreeItem { tooltipMd.appendMarkdown( "- Workbook: " + this.details.executorName + " \n", ); + } else if (this.details.isFromConnTree) { + tooltipMd.appendMarkdown("- Executiom from: Connection Tree \n"); + tooltipMd.appendMarkdown( + "- Category: " + this.details.executorName + " \n", + ); } else { tooltipMd.appendMarkdown( "- File: " + this.details.executorName + " \n", ); } - tooltipMd.appendMarkdown("- Query: \n"); - tooltipMd.appendCodeblock(this.details.query, codeType); + tooltipMd.appendMarkdown("- Query: "); + let queryText = this.details.query; + + if (typeof queryText === "string") { + const lines = queryText.split("\n"); + if (lines.length > 1) { + queryText = + lines[0].slice(0, 77) + + (lines[0].length > 77 ? "... " : "") + + "\n" + + lines[1].slice(0, 77) + + (lines[1].length > 77 ? "... " : ""); + } else { + queryText = + lines[0].slice(0, 77) + (lines[0].length > 77 ? "... " : ""); + } + } + + tooltipMd.appendCodeblock(queryText, codeType); } else { tooltipMd.appendMarkdown( "- Data Source: **" + this.details.executorName + "** \n", diff --git a/src/services/resultsPanelProvider.ts b/src/services/resultsPanelProvider.ts index ab7d1058..31dcf05a 100644 --- a/src/services/resultsPanelProvider.ts +++ b/src/services/resultsPanelProvider.ts @@ -23,11 +23,13 @@ import { ext } from "../extensionVariables"; import * as utils from "../utils/execution"; import { getNonce } from "../utils/getNonce"; import { getUri } from "../utils/getUri"; +import { kdbOutputLog } from "../utils/core"; export class KdbResultsViewProvider implements WebviewViewProvider { public static readonly viewType = "kdb-results"; - private _view?: WebviewView; + public isInsights = false; public _colorTheme: any; + private _view?: WebviewView; private _results: string | string[] = ""; constructor(private readonly _extensionUri: Uri) { @@ -48,10 +50,11 @@ export class KdbResultsViewProvider implements WebviewViewProvider { localResourceRoots: [Uri.joinPath(this._extensionUri, "out")], }; - webviewView.webview.html = this._getWebviewContent(""); + webviewView.webview.html = this._getWebviewContent(); + this.updateWebView(""); webviewView.webview.onDidReceiveMessage((data) => { - webviewView.webview.html = this._getWebviewContent(data); + this.updateWebView(data); }); webviewView.onDidChangeVisibility(() => { ext.isResultsTabVisible = webviewView.visible; @@ -62,19 +65,11 @@ export class KdbResultsViewProvider implements WebviewViewProvider { }); } - public updateResults( - queryResults: any, - isInsights?: boolean, - dataSourceType?: string, - ) { + public updateResults(queryResults: any, isInsights?: boolean) { if (this._view) { this._view.show?.(true); - this._view.webview.postMessage(queryResults); - this._view.webview.html = this._getWebviewContent( - queryResults, - isInsights, - dataSourceType, - ); + this.isInsights = !!isInsights; + this.updateWebView(queryResults); } } @@ -176,7 +171,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { } } - convertToGrid(results: any, isInsights: boolean): string { + convertToGrid(results: any, isInsights: boolean): any { const queryResult = isInsights ? results.rows : results; const columnDefs = this.generateCoumnDefs(results, isInsights); @@ -199,7 +194,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { if (rowData.length > 0) { ext.resultPanelCSV = this.convertToCsv(rowData).join("\n"); } - return JSON.stringify({ + return { defaultColDef: { sortable: true, resizable: true, @@ -217,7 +212,7 @@ export class KdbResultsViewProvider implements WebviewViewProvider { suppressContextMenu: true, suppressDragLeaveHidesColumns: true, tooltipShowDelay: 200, - }); + }; } isVisible(): boolean { @@ -251,13 +246,39 @@ export class KdbResultsViewProvider implements WebviewViewProvider { : ""; } - private _getWebviewContent( - queryResult: any, - isInsights?: boolean, - _dataSourceType?: string, - ) { + public updateWebView(queryResult: any) { ext.resultPanelCSV = ""; this._results = queryResult; + let result = ""; + let gridOptions = undefined; + if (!this._view) { + kdbOutputLog("[Results Tab] No view to update", "ERROR"); + return; + } + if (typeof queryResult === "string" || typeof queryResult === "number") { + result = + queryResult !== "" + ? `

${queryResult + .toString() + .replace(/\n/g, "
")}

` + : "

No results to show

"; + } else if (queryResult) { + gridOptions = this.convertToGrid(queryResult, this.isInsights); + } + if (gridOptions) { + this._view.webview.postMessage({ + command: "setGridOptions", + gridOptions: gridOptions, + }); + } else { + this._view.webview.postMessage({ + command: "setResultsContent", + results: result, + }); + } + } + + private _getWebviewContent() { const agGridTheme = this.defineAgGridTheme(); if (this._view) { const webviewUri = getUri(this._view.webview, this._extensionUri, [ @@ -265,70 +286,72 @@ export class KdbResultsViewProvider implements WebviewViewProvider { "webview.js", ]); const nonce = getNonce(); - let result = ""; - let gridOptionsString = ""; + return /*html*/ ` + + + + + + + + + + + Q Results + + + +
+
+
+ +
+ - - -
-
- ${result} -
-
- -
- - - - `; + function restoreColumnWidths(columnWidths) { + if (!gridApi || !columnWidths) return; + gridApi.applyColumnState({state: columnWidths}); + } + + window.addEventListener('message', event => { + const message = event.data; + if (message.command === 'setGridOptions') { + const columnWidths = saveColumnWidths(); + const gridOptions = message.gridOptions; + const gridDiv = document.getElementById('grid'); + const resultsDiv = document.querySelector('#results .content-wrapper'); + resultsDiv.innerHTML = ''; + gridDiv.innerHTML = ''; + gridApi = agGrid.createGrid(gridDiv, gridOptions); + restoreColumnWidths(columnWidths); + document.getElementById("results").scrollIntoView(); + } else if (message.command === 'setResultsContent') { + const resultsContent = message.results; + const resultsDiv = document.querySelector('#results .content-wrapper'); + const gridDiv = document.getElementById('grid'); + gridDiv.innerHTML = ''; + resultsDiv.innerHTML = ''; + resultsDiv.innerHTML = resultsContent; + } + }); + document.addEventListener('contextmenu', (e) => { + e.stopImmediatePropagation(); + }, true); + + + + `; } else { return ""; } diff --git a/src/utils/connLabel.ts b/src/utils/connLabel.ts index f2c9f4c9..790efe03 100644 --- a/src/utils/connLabel.ts +++ b/src/utils/connLabel.ts @@ -100,6 +100,9 @@ export function removeConnFromLabels(connName: string) { ); } }); + workspace + .getConfiguration() + .update("kdb.labelsConnectionMap", ext.labelConnMapList, true); } export async function handleLabelsConnMap(labels: string[], connName: string) { diff --git a/src/utils/core.ts b/src/utils/core.ts index a60a0b3b..4fd98856 100644 --- a/src/utils/core.ts +++ b/src/utils/core.ts @@ -22,12 +22,16 @@ import * as semver from "semver"; import { commands, ConfigurationTarget, Uri, window, workspace } from "vscode"; import { installTools } from "../commands/installTools"; import { ext } from "../extensionVariables"; -import { InsightDetails, Insights } from "../models/insights"; import { QueryResult } from "../models/queryResult"; -import { Server, ServerDetails } from "../models/server"; import { tryExecuteCommand } from "./cpUtils"; import { showRegistrationNotification } from "./registration"; import { Telemetry } from "./telemetryClient"; +import { + InsightDetails, + Insights, + Server, + ServerDetails, +} from "../models/connectionsModels"; export function log(childProcess: ChildProcess): void { kdbOutputLog(`Process ${childProcess.pid} started`, "INFO"); @@ -53,7 +57,8 @@ export async function checkOpenSslInstalled(): Promise { return semver.clean(installedVersion ? installedVersion[1] : ""); } } catch (err) { - kdbOutputLog(`Error in checking OpenSSL version: ${err}`, "ERROR"); + // Disabled the error, as it is not critical + // kdbOutputLog(`Error in checking OpenSSL version: ${err}`, "ERROR"); Telemetry.sendException(err as Error); } return null; @@ -312,6 +317,11 @@ export function kdbOutputLog(message: string, type: string): void { const dateNow = new Date().toLocaleDateString(); const timeNow = new Date().toLocaleTimeString(); ext.outputChannel.appendLine(`[${dateNow} ${timeNow}] [${type}] ${message}`); + if (type === "ERROR") { + window.showErrorMessage( + `Error occured, check kdb output channel for details.`, + ); + } } export function tokenUndefinedError(connLabel: string): void { @@ -319,9 +329,6 @@ export function tokenUndefinedError(connLabel: string): void { `Error retrieving access token for Insights connection named: ${connLabel}`, "ERROR", ); - window.showErrorMessage( - `Error retrieving access token for Insights connection named: ${connLabel}`, - ); } export function invalidUsernameJWT(connLabel: string): void { @@ -329,9 +336,6 @@ export function invalidUsernameJWT(connLabel: string): void { `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, "ERROR", ); - window.showErrorMessage( - `JWT did not contain a valid preferred username for Insights connection: ${connLabel}`, - ); } /* istanbul ignore next */ @@ -405,8 +409,18 @@ export function delay(ms: number) { return new Promise((resolve) => setTimeout(resolve, ms)); } -export async function checkLocalInstall(): Promise { +export async function checkLocalInstall( + isExtensionStartCheck?: boolean, +): Promise { const QHOME = workspace.getConfiguration().get("kdb.qHomeDirectory"); + if (isExtensionStartCheck) { + const notShow = workspace + .getConfiguration() + .get("kdb.neverShowQInstallAgain"); + if (notShow) { + return; + } + } if (QHOME || env.QHOME) { env.QHOME = QHOME || env.QHOME; if (!pathExists(env.QHOME!)) { @@ -454,12 +468,21 @@ export async function checkLocalInstall(): Promise { .showInformationMessage( "Local q installation not found!", "Install new instance", - "Cancel", + "No", + "Never show again", ) .then(async (installResult) => { if (installResult === "Install new instance") { await installTools(); - } else if (installResult === "Cancel") { + } else if (installResult === "Never show again") { + await workspace + .getConfiguration() + .update( + "kdb.neverShowQInstallAgain", + true, + ConfigurationTarget.Global, + ); + } else { showRegistrationNotification(); } }); diff --git a/src/utils/executionConsole.ts b/src/utils/executionConsole.ts index a6bc6d0f..7db3d675 100644 --- a/src/utils/executionConsole.ts +++ b/src/utils/executionConsole.ts @@ -13,7 +13,6 @@ import { OutputChannel, commands, window } from "vscode"; import { ext } from "../extensionVariables"; -import { ServerType } from "../models/server"; import { getHideDetailedConsoleQueryOutput, setOutputWordWrapper, @@ -23,6 +22,7 @@ import { checkIfIsDatasource, convertRowsToConsole, } from "./queryUtils"; +import { ServerType } from "../models/connectionsModels"; export class ExecutionConsole { public static current: ExecutionConsole | undefined; @@ -83,6 +83,7 @@ export class ExecutionConsole { type?: string, isPhython?: boolean, duration?: string, + isFromConnTree?: boolean, ): void { getHideDetailedConsoleQueryOutput(); const hideDetails = ext.hideDetailedConsoleQueryOutput; @@ -106,6 +107,7 @@ export class ExecutionConsole { undefined, undefined, duration, + isFromConnTree, ); } @@ -142,6 +144,7 @@ export class ExecutionConsole { isPython?: boolean, isDatasource?: boolean, duration?: string, + isFromConnTree?: boolean, ): void { getHideDetailedConsoleQueryOutput(); const hideDetails = ext.hideDetailedConsoleQueryOutput; @@ -172,6 +175,7 @@ export class ExecutionConsole { undefined, undefined, duration, + isFromConnTree, ); } } else { diff --git a/src/utils/queryUtils.ts b/src/utils/queryUtils.ts index ac60a394..a92d4fd4 100644 --- a/src/utils/queryUtils.ts +++ b/src/utils/queryUtils.ts @@ -16,12 +16,12 @@ import { join } from "path"; import { ext } from "../extensionVariables"; import { DCDS, deserialize, isCompressed, uncompress } from "../ipc/c"; import { Parse } from "../ipc/parse.qlist"; -import { ServerType } from "../models/server"; import { DDateClass, DDateTimeClass, DTimestampClass } from "../ipc/cClasses"; import { TypeBase } from "../ipc/typeBase"; import { DataSourceFiles, DataSourceTypes } from "../models/dataSource"; import { QueryHistory } from "../models/queryHistory"; import { kdbOutputLog } from "./core"; +import { ServerType } from "../models/connectionsModels"; export function sanitizeQuery(query: string): string { if (query[0] === "`") { @@ -339,6 +339,7 @@ export function addQueryHistory( isDatasource?: boolean, datasourceType?: DataSourceTypes, duration?: string, + isFromConnTree?: boolean, ) { const newQueryHistory: QueryHistory = { query: query, @@ -352,6 +353,7 @@ export function addQueryHistory( isDatasource, datasourceType, duration, + isFromConnTree, }; ext.kdbQueryHistoryList.unshift(newQueryHistory); diff --git a/src/webview/components/kdbDataSourceView.ts b/src/webview/components/kdbDataSourceView.ts index 5c8af2e6..0dae141f 100644 --- a/src/webview/components/kdbDataSourceView.ts +++ b/src/webview/components/kdbDataSourceView.ts @@ -64,6 +64,10 @@ export class KdbDataSourceView extends LitElement { selectedApi = ""; selectedTable = ""; startTS = ""; + rowLimitCount = "100000"; + isRowLimitLast = true; + rowLimit = false; + selectedServerVersion = 0; endTS = ""; fill = ""; filled = false; @@ -97,6 +101,9 @@ export class KdbDataSourceView extends LitElement { if (msg.command === DataSourceCommand.Update) { this.servers = msg.servers; this.selectedServer = msg.selectedServer; + this.selectedServerVersion = msg.selectedServerVersion + ? msg.selectedServerVersion + : 0; this.isInsights = msg.isInsights; this.isMetaLoaded = !!msg.insightsMeta.dap; this.insightsMeta = msg.insightsMeta; @@ -107,6 +114,12 @@ export class KdbDataSourceView extends LitElement { this.startTS = ds.dataSource.api.startTS; this.endTS = ds.dataSource.api.endTS; this.fill = ds.dataSource.api.fill; + this.rowLimitCount = ds.dataSource.api.rowCountLimit + ? ds.dataSource.api.rowCountLimit + : "100000"; + this.isRowLimitLast = ds.dataSource.api.isRowLimitLast + ? ds.dataSource.api.isRowLimitLast + : true; this.temporality = ds.dataSource.api.temporality; this.qsqlTarget = ds.dataSource.qsql.selectedTarget; this.qsql = ds.dataSource.qsql.query; @@ -120,6 +133,7 @@ export class KdbDataSourceView extends LitElement { this.sorts = optional.sorts; this.aggs = optional.aggs; this.groups = optional.groups; + this.rowLimit = optional.rowLimit ? optional.rowLimit : false; } this.requestUpdate(); } @@ -137,6 +151,8 @@ export class KdbDataSourceView extends LitElement { startTS: this.startTS, endTS: this.endTS, fill: this.fill, + rowCountLimit: this.rowLimitCount, + isRowLimitLast: this.isRowLimitLast, temporality: this.temporality, filter: [], groupBy: [], @@ -152,6 +168,7 @@ export class KdbDataSourceView extends LitElement { sorts: this.sorts, aggs: this.aggs, groups: this.groups, + rowLimit: this.rowLimit, }, }, qsql: { @@ -222,6 +239,78 @@ export class KdbDataSourceView extends LitElement { }); } + renderRowCountOptions() { + const compareVersions = (v1: string, v2: string) => + v1 + .split(".") + .map(Number) + .reduce((acc, num, i) => acc || num - Number(v2.split(".")[i] || 0), 0); + + if (compareVersions(this.selectedServerVersion.toString(), "1.11") >= 0) { + return html` +
+ + + + + + + +
+ `; + } else { + this.rowLimit = false; + this.rowLimitCount = "100000"; + this.isRowLimitLast = true; + } + } + renderApiOptions() { if (this.isInsights && this.isMetaLoaded) { return this.insightsMeta.api @@ -795,7 +884,7 @@ export class KdbDataSourceView extends LitElement { >End Time - + ${this.renderRowCountOptions()}
label !== "" && label !== undefined), + ), + ); + } + + addLabel() { + this.labels.push(""); + this.requestUpdate(); + } + + removeLabel(index: number) { + this.labels.splice(index, 1); + this.requestUpdate(); + } + + updateLabelValue(pos: number, event: Event) { + this.labels[pos] = (event.target as HTMLSelectElement).value; + this.requestUpdate(); + } + get selectConnection(): string { if (this.isBundledQ) { return "tab-1"; @@ -328,7 +354,36 @@ export class KdbNewConnectionView extends LitElement { `; } - renderLblDropdownOptions() { + renderLblsDropdown(pos: number) { + return html` +
+ + ${this.renderLblDropdownOptions(pos)} + + - + + +
+ `; + } + + renderLblDropdownOptions(pos: number) { return html` No Label Selected ${repeat( @@ -337,7 +392,7 @@ export class KdbNewConnectionView extends LitElement { (lbl) => html` + ?selected="${lbl.name === this.labels[pos]}">
Connection label (optional)
- @@ -863,6 +910,7 @@ export class KdbNewConnectionView extends LitElement { } private save() { + this.removeBlankLabels(); if (this.isBundledQ) { this.vscode.postMessage({ command: "kdb.newConnection.createNewBundledConnection", @@ -893,7 +941,7 @@ export class KdbNewConnectionView extends LitElement { }, }); setTimeout(() => { - this.labels[0] = this.newLblName; + this.labels.unshift(this.newLblName); this.closeModal(); }, 500); } @@ -902,6 +950,7 @@ export class KdbNewConnectionView extends LitElement { if (!this.connectionData) { return; } + this.removeBlankLabels(); if (this.connectionData.connType === 0) { this.vscode.postMessage({ command: "kdb.newConnection.editBundledConnection", diff --git a/src/webview/components/styles.ts b/src/webview/components/styles.ts index ee290445..f5b6f22e 100644 --- a/src/webview/components/styles.ts +++ b/src/webview/components/styles.ts @@ -198,4 +198,16 @@ export const newConnectionStyles = css` background: rgba(0, 0, 0, 0.5); z-index: 1000; } + + .lbl-dropdown-container { + width: 350px; + } + + .lbl-dropdown-container-field-wrapper { + margin-bottom: 0.5em; + width: 100%; + display: flex; + flex-wrap: nowrap; + justify-content: space-between; + } `; diff --git a/src/webview/styles/resultsPanel.css b/src/webview/styles/resultsPanel.css index f4c9a235..c181e93a 100644 --- a/src/webview/styles/resultsPanel.css +++ b/src/webview/styles/resultsPanel.css @@ -1,7 +1,8 @@ html, body { height: 86vh; - width: 100%; + width: 99%; + padding: 0; box-sizing: border-box; -webkit-overflow-scrolling: touch; } diff --git a/test/suite/commands.test.ts b/test/suite/commands.test.ts index 548de197..7db3db72 100644 --- a/test/suite/commands.test.ts +++ b/test/suite/commands.test.ts @@ -27,7 +27,6 @@ import { createDefaultDataSourceFile, } from "../../src/models/dataSource"; import { ExecutionTypes } from "../../src/models/execution"; -import { InsightDetails } from "../../src/models/insights"; import { ScratchpadResult } from "../../src/models/scratchpadResult"; import { InsightsNode, @@ -41,7 +40,6 @@ import * as dataSourceUtils from "../../src/utils/dataSource"; import { ExecutionConsole } from "../../src/utils/executionConsole"; import * as queryUtils from "../../src/utils/queryUtils"; import { QueryHistory } from "../../src/models/queryHistory"; -import { ServerDetails, ServerType } from "../../src/models/server"; import { NewConnectionPannel } from "../../src/panels/newConnection"; import { MAX_STR_LEN } from "../../src/validators/kdbValidator"; import { LocalConnection } from "../../src/classes/localConnection"; @@ -53,6 +51,12 @@ import { WorkspaceTreeProvider } from "../../src/services/workspaceTreeProvider" import { GetDataError } from "../../src/models/data"; import * as clientCommand from "../../src/commands/clientCommands"; import { LanguageClient } from "vscode-languageclient/node"; +import { + ExportedConnections, + InsightDetails, + ServerDetails, + ServerType, +} from "../../src/models/connectionsModels"; describe("dataSourceCommand", () => { afterEach(() => { @@ -213,6 +217,8 @@ describe("dataSourceCommand2", () => { api.startTS = "2022-01-01T00:00:00Z"; api.endTS = "2022-01-02T00:00:00Z"; api.fill = "zero"; + api.rowCountLimit = "20"; + api.isRowLimitLast = true; api.temporality = "snapshot"; api.filter = ["col1=val1;col2=val2", "col3=val3"]; api.groupBy = ["col1", "col2"]; @@ -224,6 +230,7 @@ describe("dataSourceCommand2", () => { api.optional = { filled: true, temporal: true, + rowLimit: true, filters: [], sorts: [], groups: [], @@ -237,6 +244,8 @@ describe("dataSourceCommand2", () => { startTS: "2022-01-01T00:00:00.000000000", endTS: "2022-01-02T00:00:00.000000000", fill: "zero", + limit: -20, + labels: {}, temporality: "snapshot", }); }); @@ -247,6 +256,8 @@ describe("dataSourceCommand2", () => { api.startTS = "2022-01-01T00:00:00Z"; api.endTS = "2022-01-02T00:00:00Z"; api.fill = "zero"; + api.rowCountLimit = "20"; + api.isRowLimitLast = false; api.temporality = "slice"; api.filter = []; api.groupBy = []; @@ -256,6 +267,7 @@ describe("dataSourceCommand2", () => { api.labels = []; api.table = "myTable"; api.optional = { + rowLimit: true, filled: false, temporal: true, filters: [], @@ -275,6 +287,8 @@ describe("dataSourceCommand2", () => { api.endTS = "2022-01-02T00:00:00Z"; api.fill = "zero"; api.temporality = "snapshot"; + api.rowCountLimit = "20"; + api.isRowLimitLast = false; api.filter = []; api.groupBy = []; api.agg = []; @@ -283,6 +297,7 @@ describe("dataSourceCommand2", () => { api.labels = []; api.table = "myTable"; api.optional = { + rowLimit: false, filled: true, temporal: true, filters: [ @@ -1108,6 +1123,208 @@ describe("serverCommand", () => { }); }); + describe("importConnections", () => { + let showOpenDialogStub: sinon.SinonStub; + let kdbOutputLogStub: sinon.SinonStub; + let addImportedConnectionsStub: sinon.SinonStub; + + beforeEach(() => { + showOpenDialogStub = sinon.stub(vscode.window, "showOpenDialog"); + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + addImportedConnectionsStub = sinon.stub( + serverCommand, + "addImportedConnections", + ); + }); + + afterEach(() => { + sinon.restore(); + mock.restore(); + }); + + it("should log an error if no file is selected", async () => { + showOpenDialogStub.resolves(undefined); + + await serverCommand.importConnections(); + + assert( + kdbOutputLogStub.calledWith( + "[IMPORT CONNECTION]No file selected", + "ERROR", + ), + ); + }); + }); + + describe("addImportedConnections", async () => { + let addInsightsConnectionStub: sinon.SinonStub; + let addKdbConnectionStub: sinon.SinonStub; + let kdbOutputLogStub: sinon.SinonStub; + let showInformationMessageStub: sinon.SinonStub; + let getInsightsStub: sinon.SinonStub; + let getServersStub: sinon.SinonStub; + const kdbNodeImport1: KdbNode = { + label: "local", + details: { + serverName: "testKdb", + serverAlias: "local", + serverPort: "1818", + auth: false, + managed: false, + tls: false, + }, + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextValue: "kdbNode", + children: [], + getTooltip: function (): vscode.MarkdownString { + throw new Error("Function not implemented."); + }, + getDescription: function (): string { + throw new Error("Function not implemented."); + }, + iconPath: undefined, + }; + const insightsNodeImport1: InsightsNode = { + label: "testInsight", + details: { + server: "testInsight", + alias: "testInsight", + auth: false, + }, + collapsibleState: vscode.TreeItemCollapsibleState.None, + contextValue: "insightsNode", + children: [], + getTooltip: function (): vscode.MarkdownString { + throw new Error("Function not implemented."); + }, + getDescription: function (): string { + throw new Error("Function not implemented."); + }, + iconPath: undefined, + }; + + beforeEach(() => { + addInsightsConnectionStub = sinon.stub( + serverCommand, + "addInsightsConnection", + ); + addKdbConnectionStub = sinon.stub(serverCommand, "addKdbConnection"); + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + getInsightsStub = sinon.stub(coreUtils, "getInsights").returns(undefined); + getServersStub = sinon.stub(coreUtils, "getServers").returns(undefined); + showInformationMessageStub = sinon.stub( + vscode.window, + "showInformationMessage", + ); + ext.connectionsList.length = 0; + }); + + afterEach(() => { + sinon.restore(); + ext.connectionsList.length = 0; + }); + + it("should add insights connections with unique aliases", async () => { + ext.connectionsList.push(insightsNodeImport1, kdbNodeImport1); + const importedConnections: ExportedConnections = { + connections: { + Insights: [ + { + alias: "testImportInsights1", + server: "testInsight", + auth: false, + }, + { + alias: "testImportInsights1", + server: "testInsight2", + auth: false, + }, + ], + KDB: [], + }, + }; + + await serverCommand.addImportedConnections(importedConnections); + + sinon.assert.notCalled(addKdbConnectionStub); + }); + + it("should log success message and show information message", async () => { + const importedConnections: ExportedConnections = { + connections: { + Insights: [], + KDB: [], + }, + }; + + await serverCommand.addImportedConnections(importedConnections); + + assert( + kdbOutputLogStub.calledWith( + "[IMPORT CONNECTION]Connections imported successfully", + "INFO", + ), + ); + assert( + showInformationMessageStub.calledWith( + "Connections imported successfully", + ), + ); + }); + + it("should add kdb connections", () => { + ext.connectionsList.push(insightsNodeImport1, kdbNodeImport1); + const importedConnections: ExportedConnections = { + connections: { + Insights: [], + KDB: [ + { + serverName: "testKdb", + serverAlias: "testKdb", + serverPort: "1818", + auth: false, + managed: false, + tls: false, + }, + ], + }, + }; + + serverCommand.addImportedConnections(importedConnections); + + sinon.assert.notCalled(addInsightsConnectionStub); + }); + + it("should overwrite connections", async () => { + ext.connectionsList.push(insightsNodeImport1, kdbNodeImport1); + const importedConnections: ExportedConnections = { + connections: { + Insights: [ + { + alias: "testInsight", + server: "testInsight", + auth: false, + }, + ], + KDB: [ + { + serverName: "testKdb", + serverAlias: "testKdb", + serverPort: "1818", + auth: false, + managed: false, + tls: false, + }, + ], + }, + }; + + showInformationMessageStub.returns("Overwrite"); + await serverCommand.addImportedConnections(importedConnections); + sinon.assert.notCalled(addInsightsConnectionStub); + }); + }); + describe("writeQueryResultsToView", () => { it("should call executeCommand with correct arguments", () => { const result = { data: [1, 2, 3] }; @@ -1129,7 +1346,6 @@ describe("serverCommand", () => { "kdb.resultsPanel.update", result, false, - "WORKBOOK", ); executeCommandStub.restore(); @@ -1887,6 +2103,68 @@ describe("serverCommand", () => { sinon.assert.notCalled(vscode.window.showTextDocument as sinon.SinonSpy); }); }); + + describe("exportConnections", () => { + let sandbox: sinon.SinonSandbox; + let kdbOutputLogStub: sinon.SinonStub; + + beforeEach(() => { + sandbox = sinon.createSandbox(); + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + }); + + afterEach(() => { + sandbox.restore(); + sinon.restore(); + mock.restore(); + }); + + it("should log an error when no connections are found", async () => { + const exportConnectionStub = sandbox + .stub(ConnectionManagementService.prototype, "exportConnection") + .returns(""); + const showQuickPickStub = sandbox + .stub(vscode.window, "showQuickPick") + .resolves({ label: "No" }); + + await serverCommand.exportConnections(); + + sinon.assert.calledOnce(kdbOutputLogStub); + sinon.assert.calledWith( + kdbOutputLogStub, + "[EXPORT CONNECTIONS] No connections found to be exported", + "ERROR", + ); + + exportConnectionStub.restore(); + showQuickPickStub.restore(); + }); + + it("should log info when save operation is cancelled by the user", async () => { + const exportConnectionStub = sandbox + .stub(ConnectionManagementService.prototype, "exportConnection") + .returns("{}"); + const showSaveDialogStub = sandbox + .stub(vscode.window, "showSaveDialog") + .resolves(undefined); + const showQuickPickStub = sandbox + .stub(vscode.window, "showQuickPick") + .resolves({ label: "Yes" }); + + await serverCommand.exportConnections(); + + sinon.assert.calledOnce(kdbOutputLogStub); + sinon.assert.calledWith( + kdbOutputLogStub, + "[EXPORT CONNECTIONS] Save operation was cancelled by the user", + "INFO", + ); + + exportConnectionStub.restore(); + showSaveDialogStub.restore(); + showQuickPickStub.restore(); + }); + }); }); describe("walkthroughCommand", () => { diff --git a/test/suite/panels.test.ts b/test/suite/panels.test.ts index 71570fc8..fe9d4e20 100644 --- a/test/suite/panels.test.ts +++ b/test/suite/panels.test.ts @@ -19,6 +19,7 @@ import { createDefaultDataSourceFile } from "../../src/models/dataSource"; import { DataSourcesPanel } from "../../src/panels/datasource"; import { KdbResultsViewProvider } from "../../src/services/resultsPanelProvider"; import * as utils from "../../src/utils/execution"; +import * as coreUtils from "../../src/utils/core"; import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider"; import { TreeItemCollapsibleState } from "vscode"; import { NewConnectionPannel } from "../../src/panels/newConnection"; @@ -227,7 +228,7 @@ describe("WebPanels", () => { stub.get(() => insightsConn); const output = resultsPanel.convertToGrid(results, true); - assert.equal(output, expectedOutput); + assert.equal(JSON.stringify(output), expectedOutput); // Restore the stub stub.restore(); @@ -277,7 +278,7 @@ describe("WebPanels", () => { stub.get(() => insightsConn); const output = resultsPanel.convertToGrid(results, true); - assert.equal(output, expectedOutput); + assert.equal(JSON.stringify(output), expectedOutput); // Restore the stub stub.restore(); @@ -383,6 +384,94 @@ describe("WebPanels", () => { }); }); + describe("updateWebView", () => { + const uriTest: vscode.Uri = vscode.Uri.parse("test"); + + let resultsPanel: KdbResultsViewProvider; + let postMessageStub: sinon.SinonStub; + let kdbOutputLogStub: sinon.SinonStub; + let convertToGridStub: sinon.SinonStub; + + beforeEach(() => { + resultsPanel = new KdbResultsViewProvider(uriTest); + resultsPanel["_view"] = { + webview: { + postMessage: sinon.stub(), + }, + } as any; + postMessageStub = resultsPanel["_view"].webview + .postMessage as sinon.SinonStub; + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + convertToGridStub = sinon.stub(resultsPanel, "convertToGrid"); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should log an error if there is no view to update", () => { + resultsPanel["_view"] = undefined; + resultsPanel.updateWebView("test"); + sinon.assert.calledWith( + kdbOutputLogStub, + "[Results Tab] No view to update", + "ERROR", + ); + }); + + it("should handle string queryResult", () => { + const queryResult = "test string"; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: `

test string

`, + }); + }); + + it("should handle number queryResult", () => { + const queryResult = 123; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: `

123

`, + }); + }); + + it("should handle empty string queryResult", () => { + const queryResult = ""; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: "

No results to show

", + }); + }); + + it("should handle object queryResult and call convertToGrid", () => { + const queryResult = { data: "test" }; + const gridOptions = { columnDefs: [], rowData: [] }; + convertToGridStub.returns(gridOptions); + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith( + convertToGridStub, + queryResult, + resultsPanel.isInsights, + ); + sinon.assert.calledWith(postMessageStub, { + command: "setGridOptions", + gridOptions: gridOptions, + }); + }); + + it("should handle null queryResult", () => { + const queryResult = null; + resultsPanel.updateWebView(queryResult); + sinon.assert.calledWith(postMessageStub, { + command: "setResultsContent", + results: "", + }); + }); + }); + describe("_getWebviewContent", () => { const uriTest: vscode.Uri = vscode.Uri.parse("test"); @@ -411,34 +500,9 @@ describe("WebPanels", () => { resultsPanel["_view"] = view; }); - it("returns a table", () => { - const input = [ - { id: 1, test: "test1" }, - { id: 2, test: "test2" }, - ]; - const expectedOutput = `"rowData":[{"id":1,"test":"test1"},{"id":2,"test":"test2"}],"columnDefs":[{"field":"id","headerName":"id"},{"field":"test","headerName":"test"}]`; - const stub = sinon - .stub(resultsPanel, "convertToGrid") - .returns(expectedOutput); - const actualOutput = resultsPanel["_getWebviewContent"](input); - assert.strictEqual(typeof actualOutput, "string"); - assert.ok(actualOutput.includes(expectedOutput)); - stub.restore(); - }); - - it("returns string results", () => { - const input = "Test"; - const expectedOutput = `

Test

`; - const actualOutput = resultsPanel["_getWebviewContent"](input); - assert.strictEqual(typeof actualOutput, "string"); - assert.ok(actualOutput.includes(expectedOutput)); - }); - - it("returns no results", () => { - const input = ""; - const expectedOutput = `

No results to show

`; - const actualOutput = resultsPanel["_getWebviewContent"](input); - assert.strictEqual(typeof actualOutput, "string"); + it("should render the results tab", () => { + const expectedOutput = ` id="results" class="results-view-container"`; + const actualOutput = resultsPanel["_getWebviewContent"](); assert.ok(actualOutput.includes(expectedOutput)); }); }); diff --git a/test/suite/qLangServer.test.ts b/test/suite/qLangServer.test.ts index f8eaf616..5d4e14c9 100644 --- a/test/suite/qLangServer.test.ts +++ b/test/suite/qLangServer.test.ts @@ -22,18 +22,22 @@ import { } from "vscode-languageserver"; import { TextDocument } from "vscode-languageserver-textdocument"; import QLangServer from "../../server/src/qLangServer"; +import { pathToFileURL } from "url"; const context = { includeDeclaration: true }; describe("qLangServer", () => { let server: QLangServer; + let connection: Connection; function createDocument(content: string, offset?: number) { content = content.trim(); - const document = TextDocument.create("test.q", "q", 1, content); + const uri = pathToFileURL("test.q").toString(); + const document = TextDocument.create(uri, "q", 1, content); const position = document.positionAt(offset || content.length); - const textDocument = TextDocumentIdentifier.create("test.q"); + const textDocument = TextDocumentIdentifier.create(uri); sinon.stub(server.documents, "get").value(() => document); + sinon.stub(server.documents, "all").value(() => [document]); return { textDocument, position, @@ -41,7 +45,7 @@ describe("qLangServer", () => { } beforeEach(async () => { - const connection = ({ + connection = ({ listen() {}, onDidOpenTextDocument() {}, onDidChangeTextDocument() {}, @@ -57,6 +61,20 @@ describe("qLangServer", () => { onDidChangeConfiguration() {}, onRequest() {}, onSelectionRanges() {}, + onDidChangeWatchedFiles() {}, + workspace: { + async getWorkspaceFolders() { + return []; + }, + }, + languages: { + callHierarchy: { + onPrepare() {}, + onIncomingCalls() {}, + onOutgoingCalls() {}, + }, + }, + sendDiagnostics() {}, }); const params = { @@ -80,6 +98,7 @@ describe("qLangServer", () => { assert.ok(capabilities.renameProvider); assert.ok(capabilities.completionProvider); assert.ok(capabilities.selectionRangeProvider); + assert.ok(capabilities.callHierarchyProvider); }); }); @@ -330,4 +349,74 @@ describe("qLangServer", () => { assert.strictEqual(result[0].range.end.character, 9); }); }); + + describe("onPrepareCallHierarchy", () => { + it("should prepare call hierarchy", () => { + const params = createDocument("a:1;a"); + const result = server.onPrepareCallHierarchy(params); + assert.strictEqual(result.length, 1); + }); + }); + + describe("onIncomingCallsCallHierarchy", () => { + it("should return incoming calls", () => { + const params = createDocument("a:1;a"); + const items = server.onPrepareCallHierarchy(params); + const result = server.onIncomingCallsCallHierarchy({ item: items[0] }); + assert.strictEqual(result.length, 1); + }); + }); + + describe("onOutgoingCallsCallHierarchy", () => { + it("should return outgoing calls", () => { + const params = createDocument("a:1;{a"); + const items = server.onPrepareCallHierarchy(params); + const result = server.onOutgoingCallsCallHierarchy({ item: items[0] }); + assert.strictEqual(result.length, 1); + }); + }); + + describe("setSettings", () => { + let defaultSettings = { + debug: false, + linting: false, + refactoring: "Workspace", + }; + it("should use default settings for empty", () => { + server.setSettings({}); + assert.deepEqual(server["settings"], defaultSettings); + }); + it("should use default settings for empty coming from client", () => { + server.onDidChangeConfiguration({ settings: { kdb: {} } }); + assert.deepEqual(server["settings"], defaultSettings); + }); + }); + + describe("onDidClose", () => { + it("should send epmty diagnostics", () => { + const stub = sinon.stub(connection, "sendDiagnostics"); + server.onDidClose({ document: { uri: "" } }); + assert.ok(stub.calledOnce); + }); + }); + + describe("scan", () => { + it("should scan empty workspace", () => { + sinon + .stub(connection.workspace, "getWorkspaceFolders") + .value(async () => []); + server.scan(); + assert.strictEqual(server["cached"].size, 0); + }); + }); + + describe("onDidChangeWatchedFiles", () => { + it("should parse empty match", () => { + sinon + .stub(connection.workspace, "getWorkspaceFolders") + .value(async () => []); + server.onDidChangeWatchedFiles({ changes: [] }); + assert.strictEqual(server["cached"].size, 0); + }); + }); }); diff --git a/test/suite/services.test.ts b/test/suite/services.test.ts index 93816d5e..ea205c54 100644 --- a/test/suite/services.test.ts +++ b/test/suite/services.test.ts @@ -25,9 +25,7 @@ import { workspace, } from "vscode"; import { ext } from "../../src/extensionVariables"; -import { Insights } from "../../src/models/insights"; import { QueryHistory } from "../../src/models/queryHistory"; -import { Server, ServerType } from "../../src/models/server"; import { getCurrentToken, refreshToken, @@ -65,6 +63,13 @@ import { MetaInfoType, MetaObject } from "../../src/models/meta"; import { CompletionProvider } from "../../src/services/completionProvider"; import { MetaContentProvider } from "../../src/services/metaContentProvider"; import { ConnectionLabel, Labels } from "../../src/models/labels"; +import { + Insights, + Server, + ServerType, +} from "../../src/models/connectionsModels"; +import AuthSettings from "../../src/utils/secretStorage"; +import * as coreUtils from "../../src/utils/core"; // eslint-disable-next-line @typescript-eslint/no-var-requires const codeFlow = require("../../src/services/kdbInsights/codeFlowLogin"); @@ -569,6 +574,7 @@ describe("kdbTreeProvider", () => { "nsnodedetails1", TreeItemCollapsibleState.None, "nsfullname", + "connLabel", ); assert.strictEqual( qNsNode.label, @@ -584,6 +590,7 @@ describe("kdbTreeProvider", () => { "categorynodedetails1", "categoryns", TreeItemCollapsibleState.None, + "connLabel", ); assert.strictEqual( qCategoryNode.label, @@ -599,6 +606,7 @@ describe("kdbTreeProvider", () => { "servernodedetails1", TreeItemCollapsibleState.None, "", + "connLabel", ); assert.strictEqual( qServerNode.label, @@ -821,7 +829,7 @@ describe("queryHistoryProvider", () => { executorName: "testExecutorName", connectionName: "testConnectionName", time: "testTime", - query: "testQuery", + query: `testQuery\n long test query line counter ${"a".repeat(80)}`, success: true, connectionType: ServerType.INSIGHTS, }, @@ -829,7 +837,17 @@ describe("queryHistoryProvider", () => { executorName: "testExecutorName2", connectionName: "testConnectionName2", time: "testTime2", - query: "testQuery2", + query: `testQuery2 ${"a".repeat(80)} \n testQuery2 ${"a".repeat(80)}\n testQuery2 ${"a".repeat(80)}`, + success: true, + isWorkbook: true, + connectionType: ServerType.KDB, + duration: "500", + }, + { + executorName: "testExecutorName2", + connectionName: "testConnectionName2", + time: "testTime2", + query: "testQuery2\n testQuery2\n testQuery2", success: true, isWorkbook: true, connectionType: ServerType.KDB, @@ -841,7 +859,27 @@ describe("queryHistoryProvider", () => { time: "testTime3", query: dummyDS, success: false, - connectionType: ServerType.undefined, + connectionType: ServerType.KDB, + }, + { + executorName: "variables", + connectionName: "testConnectionName2", + time: "testTime2", + query: `testQuery2 ${"a".repeat(80)}`, + success: true, + isFromConnTree: true, + connectionType: ServerType.KDB, + duration: "500", + }, + { + executorName: "variables", + connectionName: "testConnectionName2", + time: "testTime2", + query: "testQuery2", + success: true, + isFromConnTree: true, + connectionType: ServerType.KDB, + duration: "500", }, ]; beforeEach(() => { @@ -882,10 +920,25 @@ describe("queryHistoryProvider", () => { ); }); + it("Should return the KdbNode tree item element", () => { + const queryHistoryTreeItem = new QueryHistoryTreeItem( + "testLabel", + dummyQueryHistory[3], + TreeItemCollapsibleState.None, + ); + const queryHistoryProvider = new QueryHistoryProvider(); + const element = queryHistoryProvider.getTreeItem(queryHistoryTreeItem); + assert.strictEqual( + element.label, + queryHistoryTreeItem.label, + "Get query history item is incorrect", + ); + }); + it("Should return children for the tree when queryHistory has entries", async () => { const queryHistoryProvider = new QueryHistoryProvider(); const result = await queryHistoryProvider.getChildren(); - assert.strictEqual(result.length, 3, "Children count should be 3"); + assert.strictEqual(result.length, 6, "Children count should be 6"); }); it("Should not return children for the tree when queryHistory has no entries", async () => { @@ -910,7 +963,7 @@ describe("queryHistoryProvider", () => { "QueryHistoryTreeItem node creation failed", ); }); - it("Should return a new QueryHistoryTreeItem with sucess icom", () => { + it("Should return a new QueryHistoryTreeItem with sucess icon", () => { const queryHistoryTreeItem = new QueryHistoryTreeItem( "testLabel", dummyQueryHistory[0], @@ -924,10 +977,10 @@ describe("queryHistoryProvider", () => { ); }); - it("Should return a new QueryHistoryTreeItem with sucess icom", () => { + it("Should return a new QueryHistoryTreeItem with fail icon", () => { const queryHistoryTreeItem = new QueryHistoryTreeItem( "testLabel", - dummyQueryHistory[0], + dummyQueryHistory[2], TreeItemCollapsibleState.None, ); const result = queryHistoryTreeItem.defineQueryIcon(false); @@ -937,6 +990,21 @@ describe("queryHistoryProvider", () => { "QueryHistoryTreeItem defineQueryIcon failed", ); }); + + it("Should return a new QueryHistoryTreeItem with sucess icon", () => { + const queryHistoryTreeItem = new QueryHistoryTreeItem( + "testLabel", + dummyQueryHistory[3], + TreeItemCollapsibleState.None, + ); + const result = queryHistoryTreeItem.defineQueryIcon(true); + console.log(JSON.stringify(queryHistoryTreeItem)); + assert.strictEqual( + result, + sucessIcon, + "QueryHistoryTreeItem defineQueryIcon failed", + ); + }); }); }); @@ -1030,6 +1098,44 @@ describe("connectionManagerService", () => { }); }); + describe("retrieveListOfConnectionsNames", () => { + it("Should return the list of connection names", () => { + ext.connectionsList.push(kdbNode, insightNode); + const result = connectionManagerService.retrieveListOfConnectionsNames(); + assert.strictEqual(result.size, 2); + }); + }); + + describe("checkConnAlias", () => { + it("Should return localInsights when connection is insights and alias equals local", () => { + const result = connectionManagerService.checkConnAlias("local", true); + assert.strictEqual(result, "localInsights"); + }); + + it("Should note return localInsights when connection is insights and alias not equals local", () => { + const result = connectionManagerService.checkConnAlias("notLocal", true); + assert.strictEqual(result, "notLocal"); + }); + + it("Should return local when connection is kdb and alias equals local and local conn not exist already", () => { + const result = connectionManagerService.checkConnAlias( + "local", + false, + false, + ); + assert.strictEqual(result, "local"); + }); + + it("Should return localKDB when connection is kdb and alias equals local and local conn exist already", () => { + const result = connectionManagerService.checkConnAlias( + "local", + false, + true, + ); + assert.strictEqual(result, "localKDB"); + }); + }); + describe("removeConnectionFromContextString", () => { it("Should remove the connection from context string", () => { ext.connectedContextStrings.push("testLabel"); @@ -1229,35 +1335,57 @@ describe("connectionManagerService", () => { describe("resetScratchpad", () => { let connMngService: ConnectionManagementService; - let showErrorMessageStub: sinon.SinonStub; - let showInformationMessageStub: sinon.SinonStub; let resetScratchpadStub: sinon.SinonStub; + let kdbOutputLogStub: sinon.SinonStub; + let showInformationMessageStub: sinon.SinonStub; + let showErrorMessageStub: sinon.SinonStub; beforeEach(() => { connMngService = new ConnectionManagementService(); - showErrorMessageStub = sinon.stub(window, "showErrorMessage"); - showInformationMessageStub = sinon.stub(window, "showInformationMessage"); ext.activeConnection = insightsConn; resetScratchpadStub = sinon.stub(ext.activeConnection, "resetScratchpad"); + kdbOutputLogStub = sinon.stub(coreUtils, "kdbOutputLog"); + showInformationMessageStub = sinon.stub(window, "showInformationMessage"); + showErrorMessageStub = sinon.stub(window, "showErrorMessage"); }); afterEach(() => { sinon.restore(); }); - it("should call resetScratchpad on activeConnection if selection is Yes and activeConnection is an instance of InsightsConnection", async () => { + it("should log an error if there is no active connection", async () => { + ext.activeConnection = null; + await connMngService.resetScratchpad(); + sinon.assert.calledWith( + kdbOutputLogStub, + "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", + "ERROR", + ); + }); + + it("should log an error if the active connection is not an InsightsConnection", async () => { + ext.activeConnection = localConn; + await connMngService.resetScratchpad(); + sinon.assert.calledWith( + kdbOutputLogStub, + "[RESET SCRATCHPAD] Please activate an Insights connection to use this feature.", + "ERROR", + ); + }); + + it("should reset the scratchpad if the active connection is an InsightsConnection", async () => { + ext.activeConnection = insightsConn; + ext.activeConnection.insightsVersion = 1.12; showInformationMessageStub.resolves("Yes"); await connMngService.resetScratchpad(); sinon.assert.calledOnce(resetScratchpadStub); - sinon.assert.notCalled(showErrorMessageStub); }); - it("should show error message if activeConnection is not an instance of InsightsConnection", async () => { - showInformationMessageStub.resolves("Yes"); - ext.activeConnection = undefined; + it("should log an error if insightsVersion is less than or equal to 1.11", async () => { + ext.activeConnection = insightsConn; + ext.activeConnection.insightsVersion = 1.11; await connMngService.resetScratchpad(); - sinon.assert.calledOnce(showErrorMessageStub); - sinon.assert.notCalled(resetScratchpadStub); + sinon.assert.calledOnce(kdbOutputLogStub); }); }); @@ -1393,6 +1521,284 @@ describe("connectionManagerService", () => { ); }); }); + + describe("retrieveUserPass", () => { + let connectionManagerService: ConnectionManagementService; + let connectionsListStub: sinon.SinonStub; + let getAuthDataStub: sinon.SinonStub; + let kdbAuthMapStub: sinon.SinonStub; + let contextStub: sinon.SinonStub; + ext.context = {} as ExtensionContext; + + beforeEach(() => { + contextStub = sinon.stub(ext, "context").value({ + globalStorageUri: { + fsPath: "/temp/", + }, + }); + AuthSettings.init(ext.context); + ext.secretSettings = AuthSettings.instance; + connectionManagerService = new ConnectionManagementService(); + connectionsListStub = sinon.stub(ext, "connectionsList").value([]); + getAuthDataStub = sinon.stub(ext.secretSettings, "getAuthData"); + kdbAuthMapStub = sinon.stub(ext, "kdbAuthMap").value([]); + }); + + afterEach(() => { + sinon.restore(); + ext.connectionsList.length = 0; + }); + + it("should retrieve and store auth data for KdbNode connections", async () => { + ext.connectionsList.push(kdbNode); + getAuthDataStub.withArgs(kdbNode.children[0]).resolves("user1:pass1"); + + await connectionManagerService.retrieveUserPass(); + + assert.strictEqual(ext.kdbAuthMap.length, 1); + assert.deepEqual(ext.kdbAuthMap[0], { + child1: { + username: "user1", + password: "pass1", + }, + }); + }); + + it("should not store auth data if getAuthData returns null", async () => { + connectionsListStub.value([kdbNode]); + getAuthDataStub.withArgs("server1").resolves(null); + + await connectionManagerService.retrieveUserPass(); + + assert.strictEqual(ext.kdbAuthMap.length, 0); + }); + + it("should not store auth data for non-KdbNode connections", async () => { + const nonKdbNode = { children: ["server1"] }; + connectionsListStub.value([nonKdbNode]); + + await connectionManagerService.retrieveUserPass(); + + assert.strictEqual(ext.kdbAuthMap.length, 0); + }); + }); + + describe("retrieveInsightsConnVersion", () => { + let retrieveConnectionStub: sinon.SinonStub; + let connectionsListStub: sinon.SinonStub; + beforeEach(() => { + retrieveConnectionStub = sinon.stub( + connectionManagerService, + "retrieveConnectedConnection", + ); + connectionsListStub = sinon.stub(ext, "connectionsList").value([]); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should return 0 in case of non-Insights connection", async () => { + retrieveConnectionStub.withArgs("nonInsightsLabel").returns(kdbNode); + + const result = + await connectionManagerService.retrieveInsightsConnVersion( + "nonInsightsLabel", + ); + + assert.strictEqual(result, 0); + }); + + it("should return 1.11 in case of Insights connection with version", async () => { + retrieveConnectionStub.withArgs("insightsLabel").returns(insightsConn); + + const result = + await connectionManagerService.retrieveInsightsConnVersion( + "insightsLabel", + ); + + assert.strictEqual(result, 1.11); + }); + + it("should not return the version of undefined connection", async () => { + retrieveConnectionStub.withArgs("nonInsightsLabel").returns(undefined); + + const result = + await connectionManagerService.retrieveInsightsConnVersion( + "nonInsightsLabel", + ); + + assert.strictEqual(result, 0); + }); + + it("should return 0 in case of Insights with no connection version", async () => { + const insightsConn2 = new InsightsConnection( + "insightsLabel", + insightNode, + ); + retrieveConnectionStub.withArgs("insightsLabel").returns(insightsConn2); + + const result = + await connectionManagerService.retrieveInsightsConnVersion( + "insightsLabel", + ); + + assert.strictEqual(result, 0); + }); + }); + + describe("exportConnection", () => { + let retrieveConnectionStub: sinon.SinonStub; + let connectionsListStub: sinon.SinonStub; + let kdbAuthMapStub: sinon.SinonStub; + + beforeEach(() => { + retrieveConnectionStub = sinon.stub( + connectionManagerService, + "retrieveConnection", + ); + connectionsListStub = sinon.stub(ext, "connectionsList").value([]); + kdbAuthMapStub = sinon.stub(ext, "kdbAuthMap").value([]); + }); + + afterEach(() => { + sinon.restore(); + }); + + it("should return empty string if connLabel is provided and connection is not found", () => { + retrieveConnectionStub.withArgs("nonExistentLabel").returns(null); + + const result = + connectionManagerService.exportConnection("nonExistentLabel"); + + assert.strictEqual(result, ""); + }); + + it("should export KDB connection when connLabel is provided and connection is an instance of KdbNode", () => { + kdbNode.details.auth = true; + retrieveConnectionStub.withArgs("kdbLabel").returns(kdbNode); + + const result = connectionManagerService.exportConnection("kdbLabel"); + + const expectedOutput = { + connections: { + Insights: [], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should export Insights connection when connLabel is provided and connection is not an instance of KdbNode", () => { + retrieveConnectionStub.withArgs("insightsLabel").returns(insightNode); + + const result = connectionManagerService.exportConnection("insightsLabel"); + + const expectedOutput = { + connections: { + Insights: [insightNode.details], + KDB: [], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should return empty string if connLabel is not provided and connectionsList is empty", () => { + connectionsListStub.value([]); + + const result = connectionManagerService.exportConnection(); + + assert.strictEqual(result, ""); + }); + + it("should export all connections when connLabel is not provided and connectionsList contains instances of KdbNode and other connections", () => { + connectionsListStub.value([kdbNode, insightNode]); + + const result = connectionManagerService.exportConnection(); + + const expectedOutput = { + connections: { + Insights: [insightNode.details], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should set auth to false and clear username and password if includeAuth is false", () => { + connectionsListStub.value([kdbNode]); + + const result = connectionManagerService.exportConnection( + undefined, + false, + ); + + const expectedOutput = { + connections: { + Insights: [], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should set auth to true and populate username and password if includeAuth is true and auth is found", () => { + const authData = { + server1: { + username: "user1", + password: "pass1", + }, + }; + connectionsListStub.value([kdbNode]); + kdbAuthMapStub.value([authData]); + + const result = connectionManagerService.exportConnection(undefined, true); + + const expectedOutput = { + connections: { + Insights: [], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should not change auth, username, and password if includeAuth is true and auth is not found", () => { + connectionsListStub.value([kdbNode]); + kdbAuthMapStub.value([]); + + const result = connectionManagerService.exportConnection(undefined, true); + + const expectedOutput = { + connections: { + Insights: [], + KDB: [kdbNode.details], + }, + }; + + assert.strictEqual(result, JSON.stringify(expectedOutput, null, 2)); + }); + + it("should clear kdbAuthMap after processing", () => { + const authData = { + server1: { + username: "user1", + password: "pass1", + }, + }; + connectionsListStub.value([kdbNode]); + kdbAuthMapStub.value([authData]); + + connectionManagerService.exportConnection(undefined, true); + + assert.strictEqual(ext.kdbAuthMap.length, 0); + }); + }); }); describe("dataSourceEditorProvider", () => { diff --git a/test/suite/utils.test.ts b/test/suite/utils.test.ts index 5e55684a..f3fd47e8 100644 --- a/test/suite/utils.test.ts +++ b/test/suite/utils.test.ts @@ -15,12 +15,12 @@ import * as assert from "assert"; import * as sinon from "sinon"; import * as vscode from "vscode"; import mock from "mock-fs"; +import { env } from "node:process"; import { TreeItemCollapsibleState } from "vscode"; import { ext } from "../../src/extensionVariables"; import * as QTable from "../../src/ipc/QTable"; import { CancellationEvent } from "../../src/models/cancellationEvent"; import { QueryResultType } from "../../src/models/queryResult"; -import { ServerDetails, ServerType } from "../../src/models/server"; import { InsightsNode, KdbNode } from "../../src/services/kdbTreeProvider"; import { QueryHistoryProvider } from "../../src/services/queryHistoryProvider"; import * as coreUtils from "../../src/utils/core"; @@ -49,9 +49,13 @@ import { DTimestampClass, } from "../../src/ipc/cClasses"; import { DataSourceTypes } from "../../src/models/dataSource"; -import { InsightDetails } from "../../src/models/insights"; import { LocalConnection } from "../../src/classes/localConnection"; import { Labels } from "../../src/models/labels"; +import { + InsightDetails, + ServerDetails, + ServerType, +} from "../../src/models/connectionsModels"; interface ITestItem extends vscode.QuickPickItem { id: number; @@ -309,6 +313,18 @@ describe("Utils", () => { appendLineSpy.calledWithMatch(type); }); + it("kdbOutputLog should log message with date and ERROR type", () => { + const message = "test message"; + const type = "ERROR"; + + coreUtils.kdbOutputLog(message, type); + + appendLineSpy.calledOnce; + showErrorMessageSpy.calledOnce; + appendLineSpy.calledWithMatch(message); + appendLineSpy.calledWithMatch(type); + }); + it("tokenUndefinedError should log and show error message", () => { const connLabel = "test connection"; @@ -966,7 +982,6 @@ describe("Utils", () => { inputSample.rows = [{ Value: "hello" }]; const expectedOutput = [{ Value: "hello" }]; const actualOutput = queryUtils.getValueFromArray(inputSample); - console.log(JSON.stringify(actualOutput.rows)); assert.deepEqual(actualOutput.rows, expectedOutput); }); @@ -1695,6 +1710,10 @@ describe("Utils", () => { labelName: "label1", connections: ["conn1", "conn2"], }); + getConfigurationStub.returns({ + get: sinon.stub(), + update: sinon.stub(), + }); LabelsUtils.removeConnFromLabels("conn1"); @@ -1834,4 +1853,78 @@ describe("Utils", () => { assert.strictEqual(result, false); }); }); + + describe("checkLocalInstall", () => { + let getConfigurationStub: sinon.SinonStub; + let updateConfigurationStub: sinon.SinonStub; + let showInformationMessageStub: sinon.SinonStub; + let executeCommandStub: sinon.SinonStub; + let QHOME = ""; + + beforeEach(() => { + getConfigurationStub = sinon + .stub(vscode.workspace, "getConfiguration") + .returns({ + get: sinon.stub().returns(false), + update: sinon.stub().resolves(), + } as any); + updateConfigurationStub = getConfigurationStub() + .update as sinon.SinonStub; + showInformationMessageStub = sinon + .stub(vscode.window, "showInformationMessage") + .resolves(); + executeCommandStub = sinon + .stub(vscode.commands, "executeCommand") + .resolves(); + QHOME = env.QHOME; + env.QHOME = ""; + }); + + afterEach(() => { + env.QHOME = QHOME; + sinon.restore(); + }); + + it("should return if 'neverShowQInstallAgain' is true", async () => { + getConfigurationStub() + .get.withArgs("kdb.neverShowQInstallAgain") + .returns(true); + + await coreUtils.checkLocalInstall(true); + + assert.strictEqual(showInformationMessageStub.called, false); + assert.strictEqual(executeCommandStub.called, false); + }); + it("should continue if 'neverShowQInstallAgain' is false", async () => { + getConfigurationStub() + .get.withArgs("kdb.neverShowQInstallAgain") + .returns(false); + + await coreUtils.checkLocalInstall(true); + + assert.strictEqual(showInformationMessageStub.called, true); + assert.strictEqual(executeCommandStub.called, true); + }); + + it("should handle 'Never show again' response", async () => { + getConfigurationStub() + .get.withArgs("kdb.qHomeDirectory") + .returns(undefined); + getConfigurationStub() + .get.withArgs("kdb.neverShowQInstallAgain") + .returns(false); + showInformationMessageStub.resolves("Never show again"); + + await coreUtils.checkLocalInstall(); + + assert.strictEqual( + updateConfigurationStub.calledWith( + "kdb.neverShowQInstallAgain", + true, + vscode.ConfigurationTarget.Global, + ), + true, + ); + }); + }); }); diff --git a/test/suite/webview.test.ts b/test/suite/webview.test.ts index 4a5f9c7d..1bed0d54 100644 --- a/test/suite/webview.test.ts +++ b/test/suite/webview.test.ts @@ -18,12 +18,11 @@ import * as assert from "assert"; import * as sinon from "sinon"; import { KdbDataSourceView } from "../../src/webview/components/kdbDataSourceView"; import { KdbNewConnectionView } from "../../src/webview/components/kdbNewConnectionView"; -import { ServerType } from "../../src/models/server"; -import { InsightDetails } from "../../src/models/insights"; import { DataSourceCommand, DataSourceMessage2, + EditConnectionMessage, } from "../../src/models/messages"; import { DataSourceTypes, @@ -36,6 +35,7 @@ import { import { MetaObjectPayload } from "../../src/models/meta"; import { html, TemplateResult } from "lit"; import { ext } from "../../src/extensionVariables"; +import { InsightDetails, ServerType } from "../../src/models/connectionsModels"; describe("KdbDataSourceView", () => { let view: KdbDataSourceView; @@ -87,6 +87,7 @@ describe("KdbDataSourceView", () => { optional: { filled: false, temporal: false, + rowLimit: false, filters: [createFilter()], labels: [createLabel()], sorts: [createSort()], @@ -220,6 +221,20 @@ describe("KdbDataSourceView", () => { assert.ok(result); }); }); + + describe("renderRowCountOptions", () => { + it("should render row count options", () => { + view.selectedServerVersion = 1.11; + const result = view.renderRowCountOptions(); + assert.ok(result); + }); + + it("should not render row count options for older server version", () => { + view.selectedServerVersion = 1.1; + const result = view.renderRowCountOptions(); + assert.ok(!result); + }); + }); }); describe("KdbNewConnectionView", () => { @@ -420,7 +435,6 @@ describe("KdbNewConnectionView", () => { it("should render connection address for Bundled q", () => { const result = view.renderConnAddDesc(ServerType.KDB, true); - console.log(JSON.stringify(result)); assert.strictEqual( result.strings[0].includes("already set up for you"), true, @@ -484,6 +498,21 @@ describe("KdbNewConnectionView", () => { ); }); + it("should render label dropdown", () => { + view.lblNamesList = [ + { name: "label1", color: { colorHex: "#FF0000" } }, + { name: "label2", color: { colorHex: "#00FF00" } }, + ]; + view.labels = ["label1"]; + + const result = view.renderLblsDropdown(0); + + assert.strictEqual( + JSON.stringify(result).includes("No Label Selected"), + true, + ); + }); + it("should render New Label Modal", () => { const result = view.renderNewLabelModal(); @@ -538,6 +567,49 @@ describe("KdbNewConnectionView", () => { }); }); + describe("removeBlankLabels", () => { + it("should remove blank labels", () => { + view.labels = ["label1", ""]; + view.removeBlankLabels(); + assert.strictEqual(view.labels.length, 1); + }); + + it("should not remove blank labels", () => { + view.labels = ["label1"]; + view.removeBlankLabels(); + assert.strictEqual(view.labels.length, 1); + }); + + it("should remove duplicate labels", () => { + view.labels = ["label1", "label1", "label1"]; + view.removeBlankLabels(); + assert.strictEqual(view.labels.length, 1); + }); + }); + + it("should add label", () => { + view.labels = ["label1"]; + view.addLabel(); + assert.strictEqual(view.labels.length, 2); + }); + + it("should remove label", () => { + view.labels = ["label1"]; + view.removeLabel(0); + assert.strictEqual(view.labels.length, 0); + }); + + it("should update label", () => { + view.labels = ["label1"]; + const event: Event = new Event("label2"); + Object.defineProperty(event, "target", { + value: { value: "label2" }, + writable: false, + }); + view.updateLabelValue(0, event); + assert.strictEqual(view.labels[0], "label2"); + }); + describe("render()", () => { let renderServerNameStub, renderConnAddressStub, @@ -823,4 +895,76 @@ describe("KdbNewConnectionView", () => { sinon.restore(); }); }); + + describe("createLabel", () => { + let clock: sinon.SinonFakeTimers; + + beforeEach(() => { + clock = sinon.useFakeTimers(); + }); + + afterEach(() => { + clock.restore(); + }); + + it("should post a message and update labels after timeout", () => { + const api = acquireVsCodeApi(); + const postMessageStub = sinon.stub(api, "postMessage"); + const closeModalStub = sinon.stub(view, "closeModal"); + + view.newLblName = "Test Label"; + view.newLblColorName = "Test Color"; + view.labels = []; + + view.createLabel(); + + sinon.assert.calledOnce(postMessageStub); + + // Avança o tempo em 500ms + clock.tick(500); + + assert.equal(view.labels[0], "Test Label"); + sinon.assert.calledOnce(closeModalStub); + + sinon.restore(); + }); + }); + + describe("edit", () => { + const editConn: EditConnectionMessage = { + connType: 0, + serverName: "test", + serverAddress: "127.0.0.1", + }; + + it("should post a message", () => { + const api = acquireVsCodeApi(); + let result: any; + sinon.stub(api, "postMessage").value(({ command, data }) => { + if ( + command === "kdb.newConnection.editMyQConnection" || + command === "kdb.newConnection.editInsightsConnection" || + command === "kdb.newConnection.editBundledConnection" + ) { + result = data; + } + }); + view.editConnection(); + assert.ok(!result); + view.connectionData = editConn; + view.editConnection(); + assert.ok(result); + editConn.connType = 1; + view.connectionData = editConn; + view.editConnection(); + assert.ok(result); + editConn.connType = 2; + view.connectionData = editConn; + view.editConnection(); + assert.ok(result); + sinon.restore(); + }); + }); + + describe("createLabel", () => {}); });