├── .env-keep ├── .gitignore ├── .vscode ├── extensions.json ├── launch.json ├── settings.json └── tasks.json ├── .vscodeignore ├── LICENSE.md ├── README.md ├── docker-compose.yml ├── eslint.config.mjs ├── icon.png ├── justfile ├── package-lock.json ├── package.json ├── seed.sql ├── src ├── binary-finder-strategies.ts ├── binary-finder.ts ├── commands.ts ├── config.ts ├── constants.ts ├── downloader.ts ├── extension.ts ├── index.ts ├── lifecycle.ts ├── logger.ts ├── project.ts ├── releases.ts ├── session.ts ├── state.ts ├── status-bar.ts └── utils.ts ├── test └── fixtures │ ├── GLOBAL_SETUP.md │ ├── monorepo-nested-config │ ├── .vscode │ │ └── settings.json │ ├── README.md │ └── packages │ │ ├── service-a │ │ └── postgrestools.jsonc │ │ └── service-b │ │ └── test.sql │ ├── node-modules-strat │ ├── .gitignore │ ├── README.md │ ├── package-lock.json │ ├── package.json │ ├── postgrestools.jsonc │ └── src │ │ └── test.sql │ ├── simple-project-download │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── postgrestools.jsonc │ └── src │ │ └── test.sql │ ├── simple-project-global-bin │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── postgrestools.jsonc │ └── src │ │ └── test.sql │ ├── vscode-settings-strat │ ├── .gitignore │ ├── .vscode │ │ └── settings.json │ ├── README.md │ ├── configs │ │ └── postgrestools.jsonc │ └── src │ │ └── test.sql │ └── yarn-pnp-strat │ ├── .gitignore │ ├── .pnp.cjs │ ├── README.md │ ├── package.json │ ├── postgrestools.jsonc │ ├── src │ └── test.sql │ └── yarn.lock └── tsconfig.json /.env-keep: -------------------------------------------------------------------------------- 1 | SUPABASE_PUBLISH_TOKEN= -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | **/**/node_modules 2 | node_modules 3 | out 4 | .env -------------------------------------------------------------------------------- /.vscode/extensions.json: -------------------------------------------------------------------------------- 1 | { 2 | // See http://go.microsoft.com/fwlink/?LinkId=827846 3 | // for the documentation about the extensions.json format 4 | "recommendations": ["dbaeumer.vscode-eslint"] 5 | } 6 | -------------------------------------------------------------------------------- /.vscode/launch.json: -------------------------------------------------------------------------------- 1 | { 2 | "version": "0.2.0", 3 | "configurations": [ 4 | { 5 | "name": "🧩 Debug Extension (Monorepo w/ Nested Config)", 6 | "type": "extensionHost", 7 | "request": "launch", 8 | "args": [ 9 | "--disable-extensions", 10 | "--extensionDevelopmentKind=node", 11 | "--extensionDevelopmentPath=${workspaceFolder}", 12 | "--log=supabase.postgrestools:debug", 13 | "${workspaceFolder}/test/fixtures/monorepo-nested-config" 14 | ], 15 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 16 | "internalConsoleOptions": "openOnFirstSessionStart", 17 | "sourceMapRenames": true 18 | }, 19 | { 20 | "name": "🧩 Debug Extension (Node Modules Strategy)", 21 | "type": "extensionHost", 22 | "request": "launch", 23 | "args": [ 24 | "--disable-extensions", 25 | "--extensionDevelopmentKind=node", 26 | "--extensionDevelopmentPath=${workspaceFolder}", 27 | "--log=supabase.postgrestools:debug", 28 | "${workspaceFolder}/test/fixtures/node-modules-strat" 29 | ], 30 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 31 | "sourceMapRenames": true 32 | }, 33 | { 34 | "name": "🧩 Debug Extension (Simple Project Download)", 35 | "type": "extensionHost", 36 | "request": "launch", 37 | "args": [ 38 | "--disable-extensions", 39 | "--extensionDevelopmentKind=node", 40 | "--extensionDevelopmentPath=${workspaceFolder}", 41 | "--log=supabase.postgrestools:debug", 42 | "${workspaceFolder}/test/fixtures/simple-project-download" 43 | ], 44 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 45 | "sourceMapRenames": true 46 | }, 47 | { 48 | "name": "🧩 Debug Extension (VSCode Settings Strategy)", 49 | "type": "extensionHost", 50 | "request": "launch", 51 | "args": [ 52 | "--disable-extensions", 53 | "--extensionDevelopmentKind=node", 54 | "--extensionDevelopmentPath=${workspaceFolder}", 55 | "--log=supabase.postgrestools:debug", 56 | "${workspaceFolder}/test/fixtures/vscode-settings-strat" 57 | ], 58 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 59 | "sourceMapRenames": true 60 | }, 61 | { 62 | "name": "🧩 Debug Extension (Simple Project Global Binary)", 63 | "type": "extensionHost", 64 | "request": "launch", 65 | "args": [ 66 | "--disable-extensions", 67 | "--extensionDevelopmentKind=node", 68 | "--extensionDevelopmentPath=${workspaceFolder}", 69 | "--log=supabase.postgrestools:debug", 70 | "${workspaceFolder}/test/fixtures/simple-project-global-bin" 71 | ], 72 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 73 | "sourceMapRenames": true 74 | }, 75 | { 76 | "name": "🧩 Debug Extension (Yarn PnP Strategy)", 77 | "type": "extensionHost", 78 | "request": "launch", 79 | "args": [ 80 | "--disable-extensions", 81 | "--extensionDevelopmentKind=node", 82 | "--extensionDevelopmentPath=${workspaceFolder}", 83 | "--log=supabase.postgrestools:debug", 84 | "${workspaceFolder}/test/fixtures/yarn-pnp-strat" 85 | ], 86 | "outFiles": ["${workspaceFolder}/out/**/*.js"], 87 | "sourceMapRenames": true 88 | } 89 | ] 90 | } 91 | -------------------------------------------------------------------------------- /.vscode/settings.json: -------------------------------------------------------------------------------- 1 | // Place your settings in this file to overwrite default and user settings. 2 | { 3 | "files.exclude": { 4 | "out": false // set this to true to hide the "out" folder with the compiled JS files 5 | }, 6 | "search.exclude": { 7 | "out": true // set this to false to include "out" folder in search results 8 | }, 9 | // Turn off tsc task auto detection since we have the necessary tasks as npm scripts 10 | "typescript.tsc.autoDetect": "off" 11 | } 12 | -------------------------------------------------------------------------------- /.vscode/tasks.json: -------------------------------------------------------------------------------- 1 | // See https://go.microsoft.com/fwlink/?LinkId=733558 2 | // for the documentation about the tasks.json format 3 | { 4 | "version": "2.0.0", 5 | "tasks": [ 6 | { 7 | "type": "npm", 8 | "script": "watch", 9 | "problemMatcher": "$tsc-watch", 10 | "isBackground": true, 11 | "presentation": { 12 | "reveal": "never" 13 | }, 14 | "group": { 15 | "kind": "build", 16 | "isDefault": true 17 | } 18 | } 19 | ] 20 | } 21 | -------------------------------------------------------------------------------- /.vscodeignore: -------------------------------------------------------------------------------- 1 | .vscode/** 2 | .vscode-test/** 3 | src/** 4 | .gitignore 5 | .yarnrc 6 | vsc-extension-quickstart.md 7 | tsconfig.json 8 | eslint.config.mjs 9 | *.map 10 | *.ts 11 | .vscode-test.* 12 | docker-compose.yml 13 | .env 14 | .env-keep 15 | seed.sql 16 | justfile 17 | test/ 18 | CHANGELOG.md 19 | README.md 20 | 21 | -------------------------------------------------------------------------------- /LICENSE.md: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2025 Julian Domke 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 6 | 7 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 8 | 9 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 10 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # PostgresTools Extension for VS Code 2 | 3 | The **PostgresTools extension for Visual Studio Code** brings PostgreSQL inline suggestions, linting, and type checks to VSCode and VSCode-based editors. 4 | 5 | It does so by implementing an LSP client and launching an LSP Server in the background. 6 | 7 | ## What's an LSP? 8 | 9 | The [Language Server Protocol](https://microsoft.github.io/language-server-protocol/) is a protocol defined by Microsoft. Its goal is to standardize language features across many editors. If you use a modern IDE and you've ever used autocompletion or linting, you've used an LSP. The server is aware of the files you have opened and analyzes them in the background. The client sends requests for e.g. autocompletions or code actions to the server. 10 | 11 | ## Setting Up the LSP Server 12 | 13 | **First**, you need the LSP server binary. The [Postgres language server](https://github.com/supabase-community/postgres-language-server) is written in Rust and is therefore compiled to various binaries for various machines. You can set it up via one of five strategies. The extensions will check them in the following order: 14 | 15 | - The `postgrestools.bin` VSCode setting can point to a binary with relative or absolute paths. You can use this if you want to download a specific binary from one of the [Postgres language server](https://github.com/supabase-community/postgres-language-server) releases and place it in your project. 16 | - **Recommended**: If you use node, you can simply run `npm i -D @postgrestools/postgrestools@latest`. Once you restart the extension, it should look for the server binary in your `node_modules`. 17 | - If you use node but you install your packages via Yarn Plug'n'Play, you can still install `@postgrestools/postgrestools`, and the extension will check your `.pnp.cjs` file for a binary. 18 | - You can install the LSP server globally (e.g. via `brew` or by downloading a binary from the GitHub releases). Make sure that its binary is exposed in your $PATH – the extension will search it for a `postgrestools` on Darwin/Linux or a `postgrestools.exe` on Windows. 19 | - If no LSP server binary can be found via the above strategies, you will be prompted to download a binary from `postgrestools`'s GitHub Releases. You can also do this later via the [Download Server Command](#useful-commands). Note that the above strategies will still take precedence. 20 | The found binary is copied to a temporary location in your VS Code extensions folder and run from there. When you restart the extension, the copied binary will be used, and the above places won't be searched. 21 | 22 | ## Setting Up Your Project 23 | 24 | **Second**, you need a `postgrestools.jsonc` file at the root of your repository (or, use a custom file location and point to it via the `postgrestools.configFile` setting). You can find sane defaults in the [docs](https://pgtools.dev/#configuration). 25 | 26 | When you specify the `db` section, the LSP server will connect to your database and gather intel from there. This makes it possible to provide autocompletions and type checks. 27 | 28 | If you omit the section, on the other hand, those features won't be enabled, and you'll only get linting. 29 | 30 | ## Verify It Works 31 | 32 | To verify that the server is installed and running correctly, you can use the "Get Current Version" command listed in [Useful Commands](#useful-commands). 33 | 34 | To verify that the client works as intended, open a `.sql` file and write some gibberish – you should get red squiggly lines from `pg(syntax)`. 35 | 36 | ## Working With Supabase 37 | 38 | The Postgres language server is not custom-tailored to work with Supabase – it works with any Postgres database. 39 | 40 | If you do use Supabase, you can get the most of the LSP by using it against your local Supabase database (Here's how to [install the Supabase CLI](https://supabase.com/docs/guides/local-development)). 41 | 42 | Once you have [everything running locally](https://supabase.com/docs/guides/local-development/cli/getting-started), run `supabase status` to see your local `DB URL`. 43 | 44 | It'll have the following format: `postgresql://:@:/`. 45 | 46 | If you extract the values, add them to your `postgrestools.jsonc` file, and restart the extension, you should be ready to go. 47 | 48 | You can also run the LSP server against your remote database, but this might lead to small latencies and a small performance overhead (the LSP server runs `prepare` statements against your database for the type checking). 49 | 50 | You should find your remote database settings at `https://supabase.com/dashboard/project//settings/database?showConnect=true`. 51 | 52 | ## Useful Commands 53 | 54 | The extension adds seven commands to your VS Code Command Palette. They are all prefixed by `PostgresTools`. 55 | 56 | - `PostgresTools: Hard Reset (Delete All Temp and Global Binaries)` is your troubleshooting weapon. It will basically remove all binaries that were copied and downloaded _by the extension_ (not those you have installed or copied yourself). The extension will then again search for a server binary via the strategies mentioned in [the setup](#setting-up-the-lsp-server). 57 | - `PostgresTools: Download Server` lets you select and download the server binary. It'll be the matching version for your machine. If you set `postgrestools.allowDownloadPrereleases` to true in yor VS Code settings, you'll be able to select prereleases. 58 | - `PostgresTools: Get Current Version` will print the current version and the strategy with which the server binary was found. 59 | - `PostgresTools: Start` and `PostgresTools: Stop` will stop or start the LSP server and the client. 60 | - `PostgresTools: Restart` runs stop and start in succession. 61 | - `PostgresTools: Copy Latest Server Logfile` copies the latest server log file to your currently opened repo. The log file is meant to be attached to GitHub issues, it can sometimes help us to debug. 62 | 63 | ## Troubleshooting 64 | 65 | 1. First, try restarting the extension via the `PostgresTools: Hard Reset (...)` command mentioned above. 66 | 2. Open your VS Code Terminal, select the tab `Output`, and select one of the `postgrestools` extensions on the right. You might see what went wrong in the logs. 67 | 3. If you want to open a GitHub issue, it can sometimes help us if you attach the LSP server log file. We provide the `PostgresTools: Copy Latest Server Logfile` command to make that as easy as possible. 68 | 69 | ## FAQ 70 | 71 | ### I've installed the package via NPM but getting an `Error: Cannot find module '@postgrestools/cli-x86_64-windows-msvc/postgrestools.exe'`. 72 | 73 | The platform-specific package is installed as an `optionalDependency`. If it can't be installed for whatever reason, `npm` won't complain. It's likely that something went wrong during the installation. 74 | 75 | Another known issue is that npm installs the optional dependency at an unexpected location. It _should_ be located at `node_modules/@postgrestools/cli-aarch64-apple-darwin` (or another platform-specific package). 76 | 77 | If you can't find the platform specific package, please just rerun `npm install` a couple of times. 78 | 79 | If that still doesn't help, run `npm uninstall @postgrestools/postgrestools` and use the download server strategy mentioned [here.](#setting-up-the-lsp-server) 80 | 81 | ### Why am I prompted to install the PostgresTools binary? 82 | 83 | You will only be prompted if all other [strategies](#setting-up-the-lsp-server) fail. If you set out to use a different strategy, make sure the binary/node package is actually at the expected location: 84 | 85 | - Using the `postgrestool.bin` setting -> Is the binary at the configured location? 86 | - Using `npm` or `yarn` -> Are the `node_modules` installed correctly? 87 | - Using the `PATH` environment variable -> Can you run `$ postgrestools --version`, is the binary at the expected location? 88 | 89 | ## Issues 90 | 91 | If you experience any issues, please report them at the [postgres language server](https://github.com/supabase-community/postgres-language-server) repository – we'll most frequently read issues there. 92 | -------------------------------------------------------------------------------- /docker-compose.yml: -------------------------------------------------------------------------------- 1 | # Use postgres/example user/password credentials 2 | # postgresql://postgres:example@127.0.0.1:3333/postgres 3 | version: "3.9" 4 | 5 | services: 6 | db: 7 | image: postgres 8 | restart: always 9 | shm_size: 128mb 10 | environment: 11 | POSTGRES_PASSWORD: example 12 | ports: 13 | - 3333:5432 14 | -------------------------------------------------------------------------------- /eslint.config.mjs: -------------------------------------------------------------------------------- 1 | import typescriptEslint from "@typescript-eslint/eslint-plugin"; 2 | import tsParser from "@typescript-eslint/parser"; 3 | 4 | export default [{ 5 | files: ["**/*.ts"], 6 | }, { 7 | plugins: { 8 | "@typescript-eslint": typescriptEslint, 9 | }, 10 | 11 | languageOptions: { 12 | parser: tsParser, 13 | ecmaVersion: 2022, 14 | sourceType: "module", 15 | }, 16 | 17 | rules: { 18 | "@typescript-eslint/naming-convention": ["warn", { 19 | selector: "import", 20 | format: ["camelCase", "PascalCase"], 21 | }], 22 | 23 | curly: "warn", 24 | eqeqeq: "warn", 25 | "no-throw-literal": "warn", 26 | semi: "warn", 27 | }, 28 | }]; -------------------------------------------------------------------------------- /icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/supabase-community/postgrestools-vscode/88ce9ab1eba604924b53c90bb4ab5d574061f517/icon.png -------------------------------------------------------------------------------- /justfile: -------------------------------------------------------------------------------- 1 | # Run Docker Containers first 2 | init: 3 | psql "postgresql://postgres:example@127.0.0.1:3333/postgres" -f ./seed.sql 4 | 5 | connect: 6 | psql "postgresql://postgres:example@127.0.0.1:3333/postgres" -------------------------------------------------------------------------------- /package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgrestools", 3 | "version": "1.2.1", 4 | "lockfileVersion": 3, 5 | "requires": true, 6 | "packages": { 7 | "": { 8 | "name": "postgrestools", 9 | "version": "1.2.1", 10 | "dependencies": { 11 | "vscode-languageclient": "9.0.1", 12 | "vscode-uri": "3.1.0" 13 | }, 14 | "devDependencies": { 15 | "@types/node": "20.17.23", 16 | "@types/vscode": "^1.96.2", 17 | "@typescript-eslint/eslint-plugin": "^8.22.0", 18 | "@typescript-eslint/parser": "^8.22.0", 19 | "@vscode/vsce": "3.3.2", 20 | "eslint": "^9.19.0", 21 | "typescript": "^5.7.3" 22 | }, 23 | "engines": { 24 | "vscode": "^1.96.2" 25 | } 26 | }, 27 | "node_modules/@azure/abort-controller": { 28 | "version": "2.1.2", 29 | "dev": true, 30 | "license": "MIT", 31 | "dependencies": { 32 | "tslib": "^2.6.2" 33 | }, 34 | "engines": { 35 | "node": ">=18.0.0" 36 | } 37 | }, 38 | "node_modules/@azure/core-auth": { 39 | "version": "1.9.0", 40 | "dev": true, 41 | "license": "MIT", 42 | "dependencies": { 43 | "@azure/abort-controller": "^2.0.0", 44 | "@azure/core-util": "^1.11.0", 45 | "tslib": "^2.6.2" 46 | }, 47 | "engines": { 48 | "node": ">=18.0.0" 49 | } 50 | }, 51 | "node_modules/@azure/core-client": { 52 | "version": "1.9.3", 53 | "dev": true, 54 | "license": "MIT", 55 | "dependencies": { 56 | "@azure/abort-controller": "^2.0.0", 57 | "@azure/core-auth": "^1.4.0", 58 | "@azure/core-rest-pipeline": "^1.9.1", 59 | "@azure/core-tracing": "^1.0.0", 60 | "@azure/core-util": "^1.6.1", 61 | "@azure/logger": "^1.0.0", 62 | "tslib": "^2.6.2" 63 | }, 64 | "engines": { 65 | "node": ">=18.0.0" 66 | } 67 | }, 68 | "node_modules/@azure/core-rest-pipeline": { 69 | "version": "1.19.1", 70 | "dev": true, 71 | "license": "MIT", 72 | "dependencies": { 73 | "@azure/abort-controller": "^2.0.0", 74 | "@azure/core-auth": "^1.8.0", 75 | "@azure/core-tracing": "^1.0.1", 76 | "@azure/core-util": "^1.11.0", 77 | "@azure/logger": "^1.0.0", 78 | "http-proxy-agent": "^7.0.0", 79 | "https-proxy-agent": "^7.0.0", 80 | "tslib": "^2.6.2" 81 | }, 82 | "engines": { 83 | "node": ">=18.0.0" 84 | } 85 | }, 86 | "node_modules/@azure/core-tracing": { 87 | "version": "1.2.0", 88 | "dev": true, 89 | "license": "MIT", 90 | "dependencies": { 91 | "tslib": "^2.6.2" 92 | }, 93 | "engines": { 94 | "node": ">=18.0.0" 95 | } 96 | }, 97 | "node_modules/@azure/core-util": { 98 | "version": "1.11.0", 99 | "dev": true, 100 | "license": "MIT", 101 | "dependencies": { 102 | "@azure/abort-controller": "^2.0.0", 103 | "tslib": "^2.6.2" 104 | }, 105 | "engines": { 106 | "node": ">=18.0.0" 107 | } 108 | }, 109 | "node_modules/@azure/identity": { 110 | "version": "4.8.0", 111 | "dev": true, 112 | "license": "MIT", 113 | "dependencies": { 114 | "@azure/abort-controller": "^2.0.0", 115 | "@azure/core-auth": "^1.9.0", 116 | "@azure/core-client": "^1.9.2", 117 | "@azure/core-rest-pipeline": "^1.17.0", 118 | "@azure/core-tracing": "^1.0.0", 119 | "@azure/core-util": "^1.11.0", 120 | "@azure/logger": "^1.0.0", 121 | "@azure/msal-browser": "^4.2.0", 122 | "@azure/msal-node": "^3.2.3", 123 | "events": "^3.0.0", 124 | "jws": "^4.0.0", 125 | "open": "^10.1.0", 126 | "stoppable": "^1.1.0", 127 | "tslib": "^2.2.0" 128 | }, 129 | "engines": { 130 | "node": ">=18.0.0" 131 | } 132 | }, 133 | "node_modules/@azure/logger": { 134 | "version": "1.1.4", 135 | "dev": true, 136 | "license": "MIT", 137 | "dependencies": { 138 | "tslib": "^2.6.2" 139 | }, 140 | "engines": { 141 | "node": ">=18.0.0" 142 | } 143 | }, 144 | "node_modules/@azure/msal-browser": { 145 | "version": "4.8.0", 146 | "dev": true, 147 | "license": "MIT", 148 | "dependencies": { 149 | "@azure/msal-common": "15.3.0" 150 | }, 151 | "engines": { 152 | "node": ">=0.8.0" 153 | } 154 | }, 155 | "node_modules/@azure/msal-common": { 156 | "version": "15.3.0", 157 | "dev": true, 158 | "license": "MIT", 159 | "engines": { 160 | "node": ">=0.8.0" 161 | } 162 | }, 163 | "node_modules/@azure/msal-node": { 164 | "version": "3.4.0", 165 | "dev": true, 166 | "license": "MIT", 167 | "dependencies": { 168 | "@azure/msal-common": "15.3.0", 169 | "jsonwebtoken": "^9.0.0", 170 | "uuid": "^8.3.0" 171 | }, 172 | "engines": { 173 | "node": ">=16" 174 | } 175 | }, 176 | "node_modules/@eslint-community/eslint-utils": { 177 | "version": "4.4.1", 178 | "dev": true, 179 | "license": "MIT", 180 | "dependencies": { 181 | "eslint-visitor-keys": "^3.4.3" 182 | }, 183 | "engines": { 184 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 185 | }, 186 | "funding": { 187 | "url": "https://opencollective.com/eslint" 188 | }, 189 | "peerDependencies": { 190 | "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" 191 | } 192 | }, 193 | "node_modules/@eslint-community/regexpp": { 194 | "version": "4.12.1", 195 | "dev": true, 196 | "license": "MIT", 197 | "engines": { 198 | "node": "^12.0.0 || ^14.0.0 || >=16.0.0" 199 | } 200 | }, 201 | "node_modules/@eslint/config-array": { 202 | "version": "0.19.2", 203 | "dev": true, 204 | "license": "Apache-2.0", 205 | "dependencies": { 206 | "@eslint/object-schema": "^2.1.6", 207 | "debug": "^4.3.1", 208 | "minimatch": "^3.1.2" 209 | }, 210 | "engines": { 211 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 212 | } 213 | }, 214 | "node_modules/@eslint/config-array/node_modules/brace-expansion": { 215 | "version": "1.1.11", 216 | "dev": true, 217 | "license": "MIT", 218 | "dependencies": { 219 | "balanced-match": "^1.0.0", 220 | "concat-map": "0.0.1" 221 | } 222 | }, 223 | "node_modules/@eslint/config-array/node_modules/minimatch": { 224 | "version": "3.1.2", 225 | "dev": true, 226 | "license": "ISC", 227 | "dependencies": { 228 | "brace-expansion": "^1.1.7" 229 | }, 230 | "engines": { 231 | "node": "*" 232 | } 233 | }, 234 | "node_modules/@eslint/core": { 235 | "version": "0.12.0", 236 | "dev": true, 237 | "license": "Apache-2.0", 238 | "dependencies": { 239 | "@types/json-schema": "^7.0.15" 240 | }, 241 | "engines": { 242 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 243 | } 244 | }, 245 | "node_modules/@eslint/eslintrc": { 246 | "version": "3.3.0", 247 | "dev": true, 248 | "license": "MIT", 249 | "dependencies": { 250 | "ajv": "^6.12.4", 251 | "debug": "^4.3.2", 252 | "espree": "^10.0.1", 253 | "globals": "^14.0.0", 254 | "ignore": "^5.2.0", 255 | "import-fresh": "^3.2.1", 256 | "js-yaml": "^4.1.0", 257 | "minimatch": "^3.1.2", 258 | "strip-json-comments": "^3.1.1" 259 | }, 260 | "engines": { 261 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 262 | }, 263 | "funding": { 264 | "url": "https://opencollective.com/eslint" 265 | } 266 | }, 267 | "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { 268 | "version": "1.1.11", 269 | "dev": true, 270 | "license": "MIT", 271 | "dependencies": { 272 | "balanced-match": "^1.0.0", 273 | "concat-map": "0.0.1" 274 | } 275 | }, 276 | "node_modules/@eslint/eslintrc/node_modules/minimatch": { 277 | "version": "3.1.2", 278 | "dev": true, 279 | "license": "ISC", 280 | "dependencies": { 281 | "brace-expansion": "^1.1.7" 282 | }, 283 | "engines": { 284 | "node": "*" 285 | } 286 | }, 287 | "node_modules/@eslint/js": { 288 | "version": "9.21.0", 289 | "dev": true, 290 | "license": "MIT", 291 | "engines": { 292 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 293 | } 294 | }, 295 | "node_modules/@eslint/object-schema": { 296 | "version": "2.1.6", 297 | "dev": true, 298 | "license": "Apache-2.0", 299 | "engines": { 300 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 301 | } 302 | }, 303 | "node_modules/@eslint/plugin-kit": { 304 | "version": "0.2.7", 305 | "dev": true, 306 | "license": "Apache-2.0", 307 | "dependencies": { 308 | "@eslint/core": "^0.12.0", 309 | "levn": "^0.4.1" 310 | }, 311 | "engines": { 312 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 313 | } 314 | }, 315 | "node_modules/@humanfs/core": { 316 | "version": "0.19.1", 317 | "dev": true, 318 | "license": "Apache-2.0", 319 | "engines": { 320 | "node": ">=18.18.0" 321 | } 322 | }, 323 | "node_modules/@humanfs/node": { 324 | "version": "0.16.6", 325 | "dev": true, 326 | "license": "Apache-2.0", 327 | "dependencies": { 328 | "@humanfs/core": "^0.19.1", 329 | "@humanwhocodes/retry": "^0.3.0" 330 | }, 331 | "engines": { 332 | "node": ">=18.18.0" 333 | } 334 | }, 335 | "node_modules/@humanfs/node/node_modules/@humanwhocodes/retry": { 336 | "version": "0.3.1", 337 | "dev": true, 338 | "license": "Apache-2.0", 339 | "engines": { 340 | "node": ">=18.18" 341 | }, 342 | "funding": { 343 | "type": "github", 344 | "url": "https://github.com/sponsors/nzakas" 345 | } 346 | }, 347 | "node_modules/@humanwhocodes/module-importer": { 348 | "version": "1.0.1", 349 | "dev": true, 350 | "license": "Apache-2.0", 351 | "engines": { 352 | "node": ">=12.22" 353 | }, 354 | "funding": { 355 | "type": "github", 356 | "url": "https://github.com/sponsors/nzakas" 357 | } 358 | }, 359 | "node_modules/@humanwhocodes/retry": { 360 | "version": "0.4.2", 361 | "dev": true, 362 | "license": "Apache-2.0", 363 | "engines": { 364 | "node": ">=18.18" 365 | }, 366 | "funding": { 367 | "type": "github", 368 | "url": "https://github.com/sponsors/nzakas" 369 | } 370 | }, 371 | "node_modules/@isaacs/cliui": { 372 | "version": "8.0.2", 373 | "dev": true, 374 | "license": "ISC", 375 | "dependencies": { 376 | "string-width": "^5.1.2", 377 | "string-width-cjs": "npm:string-width@^4.2.0", 378 | "strip-ansi": "^7.0.1", 379 | "strip-ansi-cjs": "npm:strip-ansi@^6.0.1", 380 | "wrap-ansi": "^8.1.0", 381 | "wrap-ansi-cjs": "npm:wrap-ansi@^7.0.0" 382 | }, 383 | "engines": { 384 | "node": ">=12" 385 | } 386 | }, 387 | "node_modules/@isaacs/cliui/node_modules/ansi-styles": { 388 | "version": "6.2.1", 389 | "dev": true, 390 | "license": "MIT", 391 | "engines": { 392 | "node": ">=12" 393 | }, 394 | "funding": { 395 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 396 | } 397 | }, 398 | "node_modules/@isaacs/cliui/node_modules/emoji-regex": { 399 | "version": "9.2.2", 400 | "dev": true, 401 | "license": "MIT" 402 | }, 403 | "node_modules/@isaacs/cliui/node_modules/string-width": { 404 | "version": "5.1.2", 405 | "dev": true, 406 | "license": "MIT", 407 | "dependencies": { 408 | "eastasianwidth": "^0.2.0", 409 | "emoji-regex": "^9.2.2", 410 | "strip-ansi": "^7.0.1" 411 | }, 412 | "engines": { 413 | "node": ">=12" 414 | }, 415 | "funding": { 416 | "url": "https://github.com/sponsors/sindresorhus" 417 | } 418 | }, 419 | "node_modules/@isaacs/cliui/node_modules/strip-ansi": { 420 | "version": "7.1.0", 421 | "dev": true, 422 | "license": "MIT", 423 | "dependencies": { 424 | "ansi-regex": "^6.0.1" 425 | }, 426 | "engines": { 427 | "node": ">=12" 428 | }, 429 | "funding": { 430 | "url": "https://github.com/chalk/strip-ansi?sponsor=1" 431 | } 432 | }, 433 | "node_modules/@isaacs/cliui/node_modules/wrap-ansi": { 434 | "version": "8.1.0", 435 | "dev": true, 436 | "license": "MIT", 437 | "dependencies": { 438 | "ansi-styles": "^6.1.0", 439 | "string-width": "^5.0.1", 440 | "strip-ansi": "^7.0.1" 441 | }, 442 | "engines": { 443 | "node": ">=12" 444 | }, 445 | "funding": { 446 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 447 | } 448 | }, 449 | "node_modules/@nodelib/fs.scandir": { 450 | "version": "2.1.5", 451 | "dev": true, 452 | "license": "MIT", 453 | "dependencies": { 454 | "@nodelib/fs.stat": "2.0.5", 455 | "run-parallel": "^1.1.9" 456 | }, 457 | "engines": { 458 | "node": ">= 8" 459 | } 460 | }, 461 | "node_modules/@nodelib/fs.stat": { 462 | "version": "2.0.5", 463 | "dev": true, 464 | "license": "MIT", 465 | "engines": { 466 | "node": ">= 8" 467 | } 468 | }, 469 | "node_modules/@nodelib/fs.walk": { 470 | "version": "1.2.8", 471 | "dev": true, 472 | "license": "MIT", 473 | "dependencies": { 474 | "@nodelib/fs.scandir": "2.1.5", 475 | "fastq": "^1.6.0" 476 | }, 477 | "engines": { 478 | "node": ">= 8" 479 | } 480 | }, 481 | "node_modules/@types/estree": { 482 | "version": "1.0.6", 483 | "dev": true, 484 | "license": "MIT" 485 | }, 486 | "node_modules/@types/json-schema": { 487 | "version": "7.0.15", 488 | "dev": true, 489 | "license": "MIT" 490 | }, 491 | "node_modules/@types/node": { 492 | "version": "20.17.23", 493 | "dev": true, 494 | "license": "MIT", 495 | "dependencies": { 496 | "undici-types": "~6.19.2" 497 | } 498 | }, 499 | "node_modules/@types/vscode": { 500 | "version": "1.97.0", 501 | "dev": true, 502 | "license": "MIT" 503 | }, 504 | "node_modules/@typescript-eslint/eslint-plugin": { 505 | "version": "8.25.0", 506 | "dev": true, 507 | "license": "MIT", 508 | "dependencies": { 509 | "@eslint-community/regexpp": "^4.10.0", 510 | "@typescript-eslint/scope-manager": "8.25.0", 511 | "@typescript-eslint/type-utils": "8.25.0", 512 | "@typescript-eslint/utils": "8.25.0", 513 | "@typescript-eslint/visitor-keys": "8.25.0", 514 | "graphemer": "^1.4.0", 515 | "ignore": "^5.3.1", 516 | "natural-compare": "^1.4.0", 517 | "ts-api-utils": "^2.0.1" 518 | }, 519 | "engines": { 520 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 521 | }, 522 | "funding": { 523 | "type": "opencollective", 524 | "url": "https://opencollective.com/typescript-eslint" 525 | }, 526 | "peerDependencies": { 527 | "@typescript-eslint/parser": "^8.0.0 || ^8.0.0-alpha.0", 528 | "eslint": "^8.57.0 || ^9.0.0", 529 | "typescript": ">=4.8.4 <5.8.0" 530 | } 531 | }, 532 | "node_modules/@typescript-eslint/parser": { 533 | "version": "8.25.0", 534 | "dev": true, 535 | "license": "MIT", 536 | "dependencies": { 537 | "@typescript-eslint/scope-manager": "8.25.0", 538 | "@typescript-eslint/types": "8.25.0", 539 | "@typescript-eslint/typescript-estree": "8.25.0", 540 | "@typescript-eslint/visitor-keys": "8.25.0", 541 | "debug": "^4.3.4" 542 | }, 543 | "engines": { 544 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 545 | }, 546 | "funding": { 547 | "type": "opencollective", 548 | "url": "https://opencollective.com/typescript-eslint" 549 | }, 550 | "peerDependencies": { 551 | "eslint": "^8.57.0 || ^9.0.0", 552 | "typescript": ">=4.8.4 <5.8.0" 553 | } 554 | }, 555 | "node_modules/@typescript-eslint/scope-manager": { 556 | "version": "8.25.0", 557 | "dev": true, 558 | "license": "MIT", 559 | "dependencies": { 560 | "@typescript-eslint/types": "8.25.0", 561 | "@typescript-eslint/visitor-keys": "8.25.0" 562 | }, 563 | "engines": { 564 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 565 | }, 566 | "funding": { 567 | "type": "opencollective", 568 | "url": "https://opencollective.com/typescript-eslint" 569 | } 570 | }, 571 | "node_modules/@typescript-eslint/type-utils": { 572 | "version": "8.25.0", 573 | "dev": true, 574 | "license": "MIT", 575 | "dependencies": { 576 | "@typescript-eslint/typescript-estree": "8.25.0", 577 | "@typescript-eslint/utils": "8.25.0", 578 | "debug": "^4.3.4", 579 | "ts-api-utils": "^2.0.1" 580 | }, 581 | "engines": { 582 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 583 | }, 584 | "funding": { 585 | "type": "opencollective", 586 | "url": "https://opencollective.com/typescript-eslint" 587 | }, 588 | "peerDependencies": { 589 | "eslint": "^8.57.0 || ^9.0.0", 590 | "typescript": ">=4.8.4 <5.8.0" 591 | } 592 | }, 593 | "node_modules/@typescript-eslint/types": { 594 | "version": "8.25.0", 595 | "dev": true, 596 | "license": "MIT", 597 | "engines": { 598 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 599 | }, 600 | "funding": { 601 | "type": "opencollective", 602 | "url": "https://opencollective.com/typescript-eslint" 603 | } 604 | }, 605 | "node_modules/@typescript-eslint/typescript-estree": { 606 | "version": "8.25.0", 607 | "dev": true, 608 | "license": "MIT", 609 | "dependencies": { 610 | "@typescript-eslint/types": "8.25.0", 611 | "@typescript-eslint/visitor-keys": "8.25.0", 612 | "debug": "^4.3.4", 613 | "fast-glob": "^3.3.2", 614 | "is-glob": "^4.0.3", 615 | "minimatch": "^9.0.4", 616 | "semver": "^7.6.0", 617 | "ts-api-utils": "^2.0.1" 618 | }, 619 | "engines": { 620 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 621 | }, 622 | "funding": { 623 | "type": "opencollective", 624 | "url": "https://opencollective.com/typescript-eslint" 625 | }, 626 | "peerDependencies": { 627 | "typescript": ">=4.8.4 <5.8.0" 628 | } 629 | }, 630 | "node_modules/@typescript-eslint/utils": { 631 | "version": "8.25.0", 632 | "dev": true, 633 | "license": "MIT", 634 | "dependencies": { 635 | "@eslint-community/eslint-utils": "^4.4.0", 636 | "@typescript-eslint/scope-manager": "8.25.0", 637 | "@typescript-eslint/types": "8.25.0", 638 | "@typescript-eslint/typescript-estree": "8.25.0" 639 | }, 640 | "engines": { 641 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 642 | }, 643 | "funding": { 644 | "type": "opencollective", 645 | "url": "https://opencollective.com/typescript-eslint" 646 | }, 647 | "peerDependencies": { 648 | "eslint": "^8.57.0 || ^9.0.0", 649 | "typescript": ">=4.8.4 <5.8.0" 650 | } 651 | }, 652 | "node_modules/@typescript-eslint/visitor-keys": { 653 | "version": "8.25.0", 654 | "dev": true, 655 | "license": "MIT", 656 | "dependencies": { 657 | "@typescript-eslint/types": "8.25.0", 658 | "eslint-visitor-keys": "^4.2.0" 659 | }, 660 | "engines": { 661 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 662 | }, 663 | "funding": { 664 | "type": "opencollective", 665 | "url": "https://opencollective.com/typescript-eslint" 666 | } 667 | }, 668 | "node_modules/@typescript-eslint/visitor-keys/node_modules/eslint-visitor-keys": { 669 | "version": "4.2.0", 670 | "dev": true, 671 | "license": "Apache-2.0", 672 | "engines": { 673 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 674 | }, 675 | "funding": { 676 | "url": "https://opencollective.com/eslint" 677 | } 678 | }, 679 | "node_modules/@vscode/vsce": { 680 | "version": "3.3.2", 681 | "dev": true, 682 | "license": "MIT", 683 | "dependencies": { 684 | "@azure/identity": "^4.1.0", 685 | "@vscode/vsce-sign": "^2.0.0", 686 | "azure-devops-node-api": "^12.5.0", 687 | "chalk": "^2.4.2", 688 | "cheerio": "^1.0.0-rc.9", 689 | "cockatiel": "^3.1.2", 690 | "commander": "^12.1.0", 691 | "form-data": "^4.0.0", 692 | "glob": "^11.0.0", 693 | "hosted-git-info": "^4.0.2", 694 | "jsonc-parser": "^3.2.0", 695 | "leven": "^3.1.0", 696 | "markdown-it": "^14.1.0", 697 | "mime": "^1.3.4", 698 | "minimatch": "^3.0.3", 699 | "parse-semver": "^1.1.1", 700 | "read": "^1.0.7", 701 | "semver": "^7.5.2", 702 | "tmp": "^0.2.3", 703 | "typed-rest-client": "^1.8.4", 704 | "url-join": "^4.0.1", 705 | "xml2js": "^0.5.0", 706 | "yauzl": "^2.3.1", 707 | "yazl": "^2.2.2" 708 | }, 709 | "bin": { 710 | "vsce": "vsce" 711 | }, 712 | "engines": { 713 | "node": ">= 20" 714 | }, 715 | "optionalDependencies": { 716 | "keytar": "^7.7.0" 717 | } 718 | }, 719 | "node_modules/@vscode/vsce-sign": { 720 | "version": "2.0.5", 721 | "dev": true, 722 | "hasInstallScript": true, 723 | "license": "SEE LICENSE IN LICENSE.txt", 724 | "optionalDependencies": { 725 | "@vscode/vsce-sign-alpine-arm64": "2.0.2", 726 | "@vscode/vsce-sign-alpine-x64": "2.0.2", 727 | "@vscode/vsce-sign-darwin-arm64": "2.0.2", 728 | "@vscode/vsce-sign-darwin-x64": "2.0.2", 729 | "@vscode/vsce-sign-linux-arm": "2.0.2", 730 | "@vscode/vsce-sign-linux-arm64": "2.0.2", 731 | "@vscode/vsce-sign-linux-x64": "2.0.2", 732 | "@vscode/vsce-sign-win32-arm64": "2.0.2", 733 | "@vscode/vsce-sign-win32-x64": "2.0.2" 734 | } 735 | }, 736 | "node_modules/@vscode/vsce-sign-darwin-arm64": { 737 | "version": "2.0.2", 738 | "cpu": [ 739 | "arm64" 740 | ], 741 | "dev": true, 742 | "license": "SEE LICENSE IN LICENSE.txt", 743 | "optional": true, 744 | "os": [ 745 | "darwin" 746 | ] 747 | }, 748 | "node_modules/@vscode/vsce/node_modules/ansi-styles": { 749 | "version": "3.2.1", 750 | "dev": true, 751 | "license": "MIT", 752 | "dependencies": { 753 | "color-convert": "^1.9.0" 754 | }, 755 | "engines": { 756 | "node": ">=4" 757 | } 758 | }, 759 | "node_modules/@vscode/vsce/node_modules/brace-expansion": { 760 | "version": "1.1.11", 761 | "dev": true, 762 | "license": "MIT", 763 | "dependencies": { 764 | "balanced-match": "^1.0.0", 765 | "concat-map": "0.0.1" 766 | } 767 | }, 768 | "node_modules/@vscode/vsce/node_modules/chalk": { 769 | "version": "2.4.2", 770 | "dev": true, 771 | "license": "MIT", 772 | "dependencies": { 773 | "ansi-styles": "^3.2.1", 774 | "escape-string-regexp": "^1.0.5", 775 | "supports-color": "^5.3.0" 776 | }, 777 | "engines": { 778 | "node": ">=4" 779 | } 780 | }, 781 | "node_modules/@vscode/vsce/node_modules/color-convert": { 782 | "version": "1.9.3", 783 | "dev": true, 784 | "license": "MIT", 785 | "dependencies": { 786 | "color-name": "1.1.3" 787 | } 788 | }, 789 | "node_modules/@vscode/vsce/node_modules/color-name": { 790 | "version": "1.1.3", 791 | "dev": true, 792 | "license": "MIT" 793 | }, 794 | "node_modules/@vscode/vsce/node_modules/escape-string-regexp": { 795 | "version": "1.0.5", 796 | "dev": true, 797 | "license": "MIT", 798 | "engines": { 799 | "node": ">=0.8.0" 800 | } 801 | }, 802 | "node_modules/@vscode/vsce/node_modules/has-flag": { 803 | "version": "3.0.0", 804 | "dev": true, 805 | "license": "MIT", 806 | "engines": { 807 | "node": ">=4" 808 | } 809 | }, 810 | "node_modules/@vscode/vsce/node_modules/mime": { 811 | "version": "1.6.0", 812 | "dev": true, 813 | "license": "MIT", 814 | "bin": { 815 | "mime": "cli.js" 816 | }, 817 | "engines": { 818 | "node": ">=4" 819 | } 820 | }, 821 | "node_modules/@vscode/vsce/node_modules/minimatch": { 822 | "version": "3.1.2", 823 | "dev": true, 824 | "license": "ISC", 825 | "dependencies": { 826 | "brace-expansion": "^1.1.7" 827 | }, 828 | "engines": { 829 | "node": "*" 830 | } 831 | }, 832 | "node_modules/@vscode/vsce/node_modules/supports-color": { 833 | "version": "5.5.0", 834 | "dev": true, 835 | "license": "MIT", 836 | "dependencies": { 837 | "has-flag": "^3.0.0" 838 | }, 839 | "engines": { 840 | "node": ">=4" 841 | } 842 | }, 843 | "node_modules/@vscode/vsce/node_modules/url-join": { 844 | "version": "4.0.1", 845 | "dev": true, 846 | "license": "MIT" 847 | }, 848 | "node_modules/acorn": { 849 | "version": "8.14.0", 850 | "dev": true, 851 | "license": "MIT", 852 | "bin": { 853 | "acorn": "bin/acorn" 854 | }, 855 | "engines": { 856 | "node": ">=0.4.0" 857 | } 858 | }, 859 | "node_modules/acorn-jsx": { 860 | "version": "5.3.2", 861 | "dev": true, 862 | "license": "MIT", 863 | "peerDependencies": { 864 | "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" 865 | } 866 | }, 867 | "node_modules/agent-base": { 868 | "version": "7.1.3", 869 | "dev": true, 870 | "license": "MIT", 871 | "engines": { 872 | "node": ">= 14" 873 | } 874 | }, 875 | "node_modules/ajv": { 876 | "version": "6.12.6", 877 | "dev": true, 878 | "license": "MIT", 879 | "dependencies": { 880 | "fast-deep-equal": "^3.1.1", 881 | "fast-json-stable-stringify": "^2.0.0", 882 | "json-schema-traverse": "^0.4.1", 883 | "uri-js": "^4.2.2" 884 | }, 885 | "funding": { 886 | "type": "github", 887 | "url": "https://github.com/sponsors/epoberezkin" 888 | } 889 | }, 890 | "node_modules/ansi-regex": { 891 | "version": "6.1.0", 892 | "dev": true, 893 | "license": "MIT", 894 | "engines": { 895 | "node": ">=12" 896 | }, 897 | "funding": { 898 | "url": "https://github.com/chalk/ansi-regex?sponsor=1" 899 | } 900 | }, 901 | "node_modules/ansi-styles": { 902 | "version": "4.3.0", 903 | "dev": true, 904 | "license": "MIT", 905 | "dependencies": { 906 | "color-convert": "^2.0.1" 907 | }, 908 | "engines": { 909 | "node": ">=8" 910 | }, 911 | "funding": { 912 | "url": "https://github.com/chalk/ansi-styles?sponsor=1" 913 | } 914 | }, 915 | "node_modules/argparse": { 916 | "version": "2.0.1", 917 | "dev": true, 918 | "license": "Python-2.0" 919 | }, 920 | "node_modules/asynckit": { 921 | "version": "0.4.0", 922 | "dev": true, 923 | "license": "MIT" 924 | }, 925 | "node_modules/azure-devops-node-api": { 926 | "version": "12.5.0", 927 | "dev": true, 928 | "license": "MIT", 929 | "dependencies": { 930 | "tunnel": "0.0.6", 931 | "typed-rest-client": "^1.8.4" 932 | } 933 | }, 934 | "node_modules/balanced-match": { 935 | "version": "1.0.2", 936 | "license": "MIT" 937 | }, 938 | "node_modules/base64-js": { 939 | "version": "1.5.1", 940 | "dev": true, 941 | "funding": [ 942 | { 943 | "type": "github", 944 | "url": "https://github.com/sponsors/feross" 945 | }, 946 | { 947 | "type": "patreon", 948 | "url": "https://www.patreon.com/feross" 949 | }, 950 | { 951 | "type": "consulting", 952 | "url": "https://feross.org/support" 953 | } 954 | ], 955 | "license": "MIT", 956 | "optional": true 957 | }, 958 | "node_modules/bl": { 959 | "version": "4.1.0", 960 | "dev": true, 961 | "license": "MIT", 962 | "optional": true, 963 | "dependencies": { 964 | "buffer": "^5.5.0", 965 | "inherits": "^2.0.4", 966 | "readable-stream": "^3.4.0" 967 | } 968 | }, 969 | "node_modules/boolbase": { 970 | "version": "1.0.0", 971 | "dev": true, 972 | "license": "ISC" 973 | }, 974 | "node_modules/brace-expansion": { 975 | "version": "2.0.1", 976 | "license": "MIT", 977 | "dependencies": { 978 | "balanced-match": "^1.0.0" 979 | } 980 | }, 981 | "node_modules/braces": { 982 | "version": "3.0.3", 983 | "dev": true, 984 | "license": "MIT", 985 | "dependencies": { 986 | "fill-range": "^7.1.1" 987 | }, 988 | "engines": { 989 | "node": ">=8" 990 | } 991 | }, 992 | "node_modules/buffer": { 993 | "version": "5.7.1", 994 | "dev": true, 995 | "funding": [ 996 | { 997 | "type": "github", 998 | "url": "https://github.com/sponsors/feross" 999 | }, 1000 | { 1001 | "type": "patreon", 1002 | "url": "https://www.patreon.com/feross" 1003 | }, 1004 | { 1005 | "type": "consulting", 1006 | "url": "https://feross.org/support" 1007 | } 1008 | ], 1009 | "license": "MIT", 1010 | "optional": true, 1011 | "dependencies": { 1012 | "base64-js": "^1.3.1", 1013 | "ieee754": "^1.1.13" 1014 | } 1015 | }, 1016 | "node_modules/buffer-crc32": { 1017 | "version": "0.2.13", 1018 | "dev": true, 1019 | "license": "MIT", 1020 | "engines": { 1021 | "node": "*" 1022 | } 1023 | }, 1024 | "node_modules/buffer-equal-constant-time": { 1025 | "version": "1.0.1", 1026 | "dev": true, 1027 | "license": "BSD-3-Clause" 1028 | }, 1029 | "node_modules/bundle-name": { 1030 | "version": "4.1.0", 1031 | "dev": true, 1032 | "license": "MIT", 1033 | "dependencies": { 1034 | "run-applescript": "^7.0.0" 1035 | }, 1036 | "engines": { 1037 | "node": ">=18" 1038 | }, 1039 | "funding": { 1040 | "url": "https://github.com/sponsors/sindresorhus" 1041 | } 1042 | }, 1043 | "node_modules/call-bind-apply-helpers": { 1044 | "version": "1.0.2", 1045 | "dev": true, 1046 | "license": "MIT", 1047 | "dependencies": { 1048 | "es-errors": "^1.3.0", 1049 | "function-bind": "^1.1.2" 1050 | }, 1051 | "engines": { 1052 | "node": ">= 0.4" 1053 | } 1054 | }, 1055 | "node_modules/call-bound": { 1056 | "version": "1.0.4", 1057 | "dev": true, 1058 | "license": "MIT", 1059 | "dependencies": { 1060 | "call-bind-apply-helpers": "^1.0.2", 1061 | "get-intrinsic": "^1.3.0" 1062 | }, 1063 | "engines": { 1064 | "node": ">= 0.4" 1065 | }, 1066 | "funding": { 1067 | "url": "https://github.com/sponsors/ljharb" 1068 | } 1069 | }, 1070 | "node_modules/callsites": { 1071 | "version": "3.1.0", 1072 | "dev": true, 1073 | "license": "MIT", 1074 | "engines": { 1075 | "node": ">=6" 1076 | } 1077 | }, 1078 | "node_modules/chalk": { 1079 | "version": "4.1.2", 1080 | "dev": true, 1081 | "license": "MIT", 1082 | "dependencies": { 1083 | "ansi-styles": "^4.1.0", 1084 | "supports-color": "^7.1.0" 1085 | }, 1086 | "engines": { 1087 | "node": ">=10" 1088 | }, 1089 | "funding": { 1090 | "url": "https://github.com/chalk/chalk?sponsor=1" 1091 | } 1092 | }, 1093 | "node_modules/cheerio": { 1094 | "version": "1.0.0", 1095 | "dev": true, 1096 | "license": "MIT", 1097 | "dependencies": { 1098 | "cheerio-select": "^2.1.0", 1099 | "dom-serializer": "^2.0.0", 1100 | "domhandler": "^5.0.3", 1101 | "domutils": "^3.1.0", 1102 | "encoding-sniffer": "^0.2.0", 1103 | "htmlparser2": "^9.1.0", 1104 | "parse5": "^7.1.2", 1105 | "parse5-htmlparser2-tree-adapter": "^7.0.0", 1106 | "parse5-parser-stream": "^7.1.2", 1107 | "undici": "^6.19.5", 1108 | "whatwg-mimetype": "^4.0.0" 1109 | }, 1110 | "engines": { 1111 | "node": ">=18.17" 1112 | }, 1113 | "funding": { 1114 | "url": "https://github.com/cheeriojs/cheerio?sponsor=1" 1115 | } 1116 | }, 1117 | "node_modules/cheerio-select": { 1118 | "version": "2.1.0", 1119 | "dev": true, 1120 | "license": "BSD-2-Clause", 1121 | "dependencies": { 1122 | "boolbase": "^1.0.0", 1123 | "css-select": "^5.1.0", 1124 | "css-what": "^6.1.0", 1125 | "domelementtype": "^2.3.0", 1126 | "domhandler": "^5.0.3", 1127 | "domutils": "^3.0.1" 1128 | }, 1129 | "funding": { 1130 | "url": "https://github.com/sponsors/fb55" 1131 | } 1132 | }, 1133 | "node_modules/cheerio/node_modules/parse5": { 1134 | "version": "7.2.1", 1135 | "dev": true, 1136 | "license": "MIT", 1137 | "dependencies": { 1138 | "entities": "^4.5.0" 1139 | }, 1140 | "funding": { 1141 | "url": "https://github.com/inikulin/parse5?sponsor=1" 1142 | } 1143 | }, 1144 | "node_modules/cheerio/node_modules/parse5-htmlparser2-tree-adapter": { 1145 | "version": "7.1.0", 1146 | "dev": true, 1147 | "license": "MIT", 1148 | "dependencies": { 1149 | "domhandler": "^5.0.3", 1150 | "parse5": "^7.0.0" 1151 | }, 1152 | "funding": { 1153 | "url": "https://github.com/inikulin/parse5?sponsor=1" 1154 | } 1155 | }, 1156 | "node_modules/chownr": { 1157 | "version": "1.1.4", 1158 | "dev": true, 1159 | "license": "ISC", 1160 | "optional": true 1161 | }, 1162 | "node_modules/cockatiel": { 1163 | "version": "3.2.1", 1164 | "dev": true, 1165 | "license": "MIT", 1166 | "engines": { 1167 | "node": ">=16" 1168 | } 1169 | }, 1170 | "node_modules/color-convert": { 1171 | "version": "2.0.1", 1172 | "dev": true, 1173 | "license": "MIT", 1174 | "dependencies": { 1175 | "color-name": "~1.1.4" 1176 | }, 1177 | "engines": { 1178 | "node": ">=7.0.0" 1179 | } 1180 | }, 1181 | "node_modules/color-name": { 1182 | "version": "1.1.4", 1183 | "dev": true, 1184 | "license": "MIT" 1185 | }, 1186 | "node_modules/combined-stream": { 1187 | "version": "1.0.8", 1188 | "dev": true, 1189 | "license": "MIT", 1190 | "dependencies": { 1191 | "delayed-stream": "~1.0.0" 1192 | }, 1193 | "engines": { 1194 | "node": ">= 0.8" 1195 | } 1196 | }, 1197 | "node_modules/commander": { 1198 | "version": "12.1.0", 1199 | "dev": true, 1200 | "license": "MIT", 1201 | "engines": { 1202 | "node": ">=18" 1203 | } 1204 | }, 1205 | "node_modules/concat-map": { 1206 | "version": "0.0.1", 1207 | "dev": true, 1208 | "license": "MIT" 1209 | }, 1210 | "node_modules/cross-spawn": { 1211 | "version": "7.0.6", 1212 | "dev": true, 1213 | "license": "MIT", 1214 | "dependencies": { 1215 | "path-key": "^3.1.0", 1216 | "shebang-command": "^2.0.0", 1217 | "which": "^2.0.1" 1218 | }, 1219 | "engines": { 1220 | "node": ">= 8" 1221 | } 1222 | }, 1223 | "node_modules/css-select": { 1224 | "version": "5.1.0", 1225 | "dev": true, 1226 | "license": "BSD-2-Clause", 1227 | "dependencies": { 1228 | "boolbase": "^1.0.0", 1229 | "css-what": "^6.1.0", 1230 | "domhandler": "^5.0.2", 1231 | "domutils": "^3.0.1", 1232 | "nth-check": "^2.0.1" 1233 | }, 1234 | "funding": { 1235 | "url": "https://github.com/sponsors/fb55" 1236 | } 1237 | }, 1238 | "node_modules/css-what": { 1239 | "version": "6.1.0", 1240 | "dev": true, 1241 | "license": "BSD-2-Clause", 1242 | "engines": { 1243 | "node": ">= 6" 1244 | }, 1245 | "funding": { 1246 | "url": "https://github.com/sponsors/fb55" 1247 | } 1248 | }, 1249 | "node_modules/debug": { 1250 | "version": "4.4.0", 1251 | "dev": true, 1252 | "license": "MIT", 1253 | "dependencies": { 1254 | "ms": "^2.1.3" 1255 | }, 1256 | "engines": { 1257 | "node": ">=6.0" 1258 | }, 1259 | "peerDependenciesMeta": { 1260 | "supports-color": { 1261 | "optional": true 1262 | } 1263 | } 1264 | }, 1265 | "node_modules/decompress-response": { 1266 | "version": "6.0.0", 1267 | "dev": true, 1268 | "license": "MIT", 1269 | "optional": true, 1270 | "dependencies": { 1271 | "mimic-response": "^3.1.0" 1272 | }, 1273 | "engines": { 1274 | "node": ">=10" 1275 | }, 1276 | "funding": { 1277 | "url": "https://github.com/sponsors/sindresorhus" 1278 | } 1279 | }, 1280 | "node_modules/deep-extend": { 1281 | "version": "0.6.0", 1282 | "dev": true, 1283 | "license": "MIT", 1284 | "optional": true, 1285 | "engines": { 1286 | "node": ">=4.0.0" 1287 | } 1288 | }, 1289 | "node_modules/deep-is": { 1290 | "version": "0.1.4", 1291 | "dev": true, 1292 | "license": "MIT" 1293 | }, 1294 | "node_modules/default-browser": { 1295 | "version": "5.2.1", 1296 | "dev": true, 1297 | "license": "MIT", 1298 | "dependencies": { 1299 | "bundle-name": "^4.1.0", 1300 | "default-browser-id": "^5.0.0" 1301 | }, 1302 | "engines": { 1303 | "node": ">=18" 1304 | }, 1305 | "funding": { 1306 | "url": "https://github.com/sponsors/sindresorhus" 1307 | } 1308 | }, 1309 | "node_modules/default-browser-id": { 1310 | "version": "5.0.0", 1311 | "dev": true, 1312 | "license": "MIT", 1313 | "engines": { 1314 | "node": ">=18" 1315 | }, 1316 | "funding": { 1317 | "url": "https://github.com/sponsors/sindresorhus" 1318 | } 1319 | }, 1320 | "node_modules/define-lazy-prop": { 1321 | "version": "3.0.0", 1322 | "dev": true, 1323 | "license": "MIT", 1324 | "engines": { 1325 | "node": ">=12" 1326 | }, 1327 | "funding": { 1328 | "url": "https://github.com/sponsors/sindresorhus" 1329 | } 1330 | }, 1331 | "node_modules/delayed-stream": { 1332 | "version": "1.0.0", 1333 | "dev": true, 1334 | "license": "MIT", 1335 | "engines": { 1336 | "node": ">=0.4.0" 1337 | } 1338 | }, 1339 | "node_modules/detect-libc": { 1340 | "version": "2.0.3", 1341 | "dev": true, 1342 | "license": "Apache-2.0", 1343 | "optional": true, 1344 | "engines": { 1345 | "node": ">=8" 1346 | } 1347 | }, 1348 | "node_modules/dom-serializer": { 1349 | "version": "2.0.0", 1350 | "dev": true, 1351 | "license": "MIT", 1352 | "dependencies": { 1353 | "domelementtype": "^2.3.0", 1354 | "domhandler": "^5.0.2", 1355 | "entities": "^4.2.0" 1356 | }, 1357 | "funding": { 1358 | "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" 1359 | } 1360 | }, 1361 | "node_modules/domelementtype": { 1362 | "version": "2.3.0", 1363 | "dev": true, 1364 | "funding": [ 1365 | { 1366 | "type": "github", 1367 | "url": "https://github.com/sponsors/fb55" 1368 | } 1369 | ], 1370 | "license": "BSD-2-Clause" 1371 | }, 1372 | "node_modules/domhandler": { 1373 | "version": "5.0.3", 1374 | "dev": true, 1375 | "license": "BSD-2-Clause", 1376 | "dependencies": { 1377 | "domelementtype": "^2.3.0" 1378 | }, 1379 | "engines": { 1380 | "node": ">= 4" 1381 | }, 1382 | "funding": { 1383 | "url": "https://github.com/fb55/domhandler?sponsor=1" 1384 | } 1385 | }, 1386 | "node_modules/domutils": { 1387 | "version": "3.2.2", 1388 | "dev": true, 1389 | "license": "BSD-2-Clause", 1390 | "dependencies": { 1391 | "dom-serializer": "^2.0.0", 1392 | "domelementtype": "^2.3.0", 1393 | "domhandler": "^5.0.3" 1394 | }, 1395 | "funding": { 1396 | "url": "https://github.com/fb55/domutils?sponsor=1" 1397 | } 1398 | }, 1399 | "node_modules/dunder-proto": { 1400 | "version": "1.0.1", 1401 | "dev": true, 1402 | "license": "MIT", 1403 | "dependencies": { 1404 | "call-bind-apply-helpers": "^1.0.1", 1405 | "es-errors": "^1.3.0", 1406 | "gopd": "^1.2.0" 1407 | }, 1408 | "engines": { 1409 | "node": ">= 0.4" 1410 | } 1411 | }, 1412 | "node_modules/eastasianwidth": { 1413 | "version": "0.2.0", 1414 | "dev": true, 1415 | "license": "MIT" 1416 | }, 1417 | "node_modules/ecdsa-sig-formatter": { 1418 | "version": "1.0.11", 1419 | "dev": true, 1420 | "license": "Apache-2.0", 1421 | "dependencies": { 1422 | "safe-buffer": "^5.0.1" 1423 | } 1424 | }, 1425 | "node_modules/emoji-regex": { 1426 | "version": "8.0.0", 1427 | "dev": true, 1428 | "license": "MIT" 1429 | }, 1430 | "node_modules/encoding-sniffer": { 1431 | "version": "0.2.0", 1432 | "dev": true, 1433 | "license": "MIT", 1434 | "dependencies": { 1435 | "iconv-lite": "^0.6.3", 1436 | "whatwg-encoding": "^3.1.1" 1437 | }, 1438 | "funding": { 1439 | "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" 1440 | } 1441 | }, 1442 | "node_modules/end-of-stream": { 1443 | "version": "1.4.4", 1444 | "dev": true, 1445 | "license": "MIT", 1446 | "optional": true, 1447 | "dependencies": { 1448 | "once": "^1.4.0" 1449 | } 1450 | }, 1451 | "node_modules/entities": { 1452 | "version": "4.5.0", 1453 | "dev": true, 1454 | "license": "BSD-2-Clause", 1455 | "engines": { 1456 | "node": ">=0.12" 1457 | }, 1458 | "funding": { 1459 | "url": "https://github.com/fb55/entities?sponsor=1" 1460 | } 1461 | }, 1462 | "node_modules/es-define-property": { 1463 | "version": "1.0.1", 1464 | "dev": true, 1465 | "license": "MIT", 1466 | "engines": { 1467 | "node": ">= 0.4" 1468 | } 1469 | }, 1470 | "node_modules/es-errors": { 1471 | "version": "1.3.0", 1472 | "dev": true, 1473 | "license": "MIT", 1474 | "engines": { 1475 | "node": ">= 0.4" 1476 | } 1477 | }, 1478 | "node_modules/es-object-atoms": { 1479 | "version": "1.1.1", 1480 | "dev": true, 1481 | "license": "MIT", 1482 | "dependencies": { 1483 | "es-errors": "^1.3.0" 1484 | }, 1485 | "engines": { 1486 | "node": ">= 0.4" 1487 | } 1488 | }, 1489 | "node_modules/es-set-tostringtag": { 1490 | "version": "2.1.0", 1491 | "dev": true, 1492 | "license": "MIT", 1493 | "dependencies": { 1494 | "es-errors": "^1.3.0", 1495 | "get-intrinsic": "^1.2.6", 1496 | "has-tostringtag": "^1.0.2", 1497 | "hasown": "^2.0.2" 1498 | }, 1499 | "engines": { 1500 | "node": ">= 0.4" 1501 | } 1502 | }, 1503 | "node_modules/escape-string-regexp": { 1504 | "version": "4.0.0", 1505 | "dev": true, 1506 | "license": "MIT", 1507 | "engines": { 1508 | "node": ">=10" 1509 | }, 1510 | "funding": { 1511 | "url": "https://github.com/sponsors/sindresorhus" 1512 | } 1513 | }, 1514 | "node_modules/eslint": { 1515 | "version": "9.21.0", 1516 | "dev": true, 1517 | "license": "MIT", 1518 | "dependencies": { 1519 | "@eslint-community/eslint-utils": "^4.2.0", 1520 | "@eslint-community/regexpp": "^4.12.1", 1521 | "@eslint/config-array": "^0.19.2", 1522 | "@eslint/core": "^0.12.0", 1523 | "@eslint/eslintrc": "^3.3.0", 1524 | "@eslint/js": "9.21.0", 1525 | "@eslint/plugin-kit": "^0.2.7", 1526 | "@humanfs/node": "^0.16.6", 1527 | "@humanwhocodes/module-importer": "^1.0.1", 1528 | "@humanwhocodes/retry": "^0.4.2", 1529 | "@types/estree": "^1.0.6", 1530 | "@types/json-schema": "^7.0.15", 1531 | "ajv": "^6.12.4", 1532 | "chalk": "^4.0.0", 1533 | "cross-spawn": "^7.0.6", 1534 | "debug": "^4.3.2", 1535 | "escape-string-regexp": "^4.0.0", 1536 | "eslint-scope": "^8.2.0", 1537 | "eslint-visitor-keys": "^4.2.0", 1538 | "espree": "^10.3.0", 1539 | "esquery": "^1.5.0", 1540 | "esutils": "^2.0.2", 1541 | "fast-deep-equal": "^3.1.3", 1542 | "file-entry-cache": "^8.0.0", 1543 | "find-up": "^5.0.0", 1544 | "glob-parent": "^6.0.2", 1545 | "ignore": "^5.2.0", 1546 | "imurmurhash": "^0.1.4", 1547 | "is-glob": "^4.0.0", 1548 | "json-stable-stringify-without-jsonify": "^1.0.1", 1549 | "lodash.merge": "^4.6.2", 1550 | "minimatch": "^3.1.2", 1551 | "natural-compare": "^1.4.0", 1552 | "optionator": "^0.9.3" 1553 | }, 1554 | "bin": { 1555 | "eslint": "bin/eslint.js" 1556 | }, 1557 | "engines": { 1558 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1559 | }, 1560 | "funding": { 1561 | "url": "https://eslint.org/donate" 1562 | }, 1563 | "peerDependencies": { 1564 | "jiti": "*" 1565 | }, 1566 | "peerDependenciesMeta": { 1567 | "jiti": { 1568 | "optional": true 1569 | } 1570 | } 1571 | }, 1572 | "node_modules/eslint-scope": { 1573 | "version": "8.2.0", 1574 | "dev": true, 1575 | "license": "BSD-2-Clause", 1576 | "dependencies": { 1577 | "esrecurse": "^4.3.0", 1578 | "estraverse": "^5.2.0" 1579 | }, 1580 | "engines": { 1581 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1582 | }, 1583 | "funding": { 1584 | "url": "https://opencollective.com/eslint" 1585 | } 1586 | }, 1587 | "node_modules/eslint-visitor-keys": { 1588 | "version": "3.4.3", 1589 | "dev": true, 1590 | "license": "Apache-2.0", 1591 | "engines": { 1592 | "node": "^12.22.0 || ^14.17.0 || >=16.0.0" 1593 | }, 1594 | "funding": { 1595 | "url": "https://opencollective.com/eslint" 1596 | } 1597 | }, 1598 | "node_modules/eslint/node_modules/brace-expansion": { 1599 | "version": "1.1.11", 1600 | "dev": true, 1601 | "license": "MIT", 1602 | "dependencies": { 1603 | "balanced-match": "^1.0.0", 1604 | "concat-map": "0.0.1" 1605 | } 1606 | }, 1607 | "node_modules/eslint/node_modules/eslint-visitor-keys": { 1608 | "version": "4.2.0", 1609 | "dev": true, 1610 | "license": "Apache-2.0", 1611 | "engines": { 1612 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1613 | }, 1614 | "funding": { 1615 | "url": "https://opencollective.com/eslint" 1616 | } 1617 | }, 1618 | "node_modules/eslint/node_modules/glob-parent": { 1619 | "version": "6.0.2", 1620 | "dev": true, 1621 | "license": "ISC", 1622 | "dependencies": { 1623 | "is-glob": "^4.0.3" 1624 | }, 1625 | "engines": { 1626 | "node": ">=10.13.0" 1627 | } 1628 | }, 1629 | "node_modules/eslint/node_modules/minimatch": { 1630 | "version": "3.1.2", 1631 | "dev": true, 1632 | "license": "ISC", 1633 | "dependencies": { 1634 | "brace-expansion": "^1.1.7" 1635 | }, 1636 | "engines": { 1637 | "node": "*" 1638 | } 1639 | }, 1640 | "node_modules/espree": { 1641 | "version": "10.3.0", 1642 | "dev": true, 1643 | "license": "BSD-2-Clause", 1644 | "dependencies": { 1645 | "acorn": "^8.14.0", 1646 | "acorn-jsx": "^5.3.2", 1647 | "eslint-visitor-keys": "^4.2.0" 1648 | }, 1649 | "engines": { 1650 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1651 | }, 1652 | "funding": { 1653 | "url": "https://opencollective.com/eslint" 1654 | } 1655 | }, 1656 | "node_modules/espree/node_modules/eslint-visitor-keys": { 1657 | "version": "4.2.0", 1658 | "dev": true, 1659 | "license": "Apache-2.0", 1660 | "engines": { 1661 | "node": "^18.18.0 || ^20.9.0 || >=21.1.0" 1662 | }, 1663 | "funding": { 1664 | "url": "https://opencollective.com/eslint" 1665 | } 1666 | }, 1667 | "node_modules/esquery": { 1668 | "version": "1.6.0", 1669 | "dev": true, 1670 | "license": "BSD-3-Clause", 1671 | "dependencies": { 1672 | "estraverse": "^5.1.0" 1673 | }, 1674 | "engines": { 1675 | "node": ">=0.10" 1676 | } 1677 | }, 1678 | "node_modules/esrecurse": { 1679 | "version": "4.3.0", 1680 | "dev": true, 1681 | "license": "BSD-2-Clause", 1682 | "dependencies": { 1683 | "estraverse": "^5.2.0" 1684 | }, 1685 | "engines": { 1686 | "node": ">=4.0" 1687 | } 1688 | }, 1689 | "node_modules/estraverse": { 1690 | "version": "5.3.0", 1691 | "dev": true, 1692 | "license": "BSD-2-Clause", 1693 | "engines": { 1694 | "node": ">=4.0" 1695 | } 1696 | }, 1697 | "node_modules/esutils": { 1698 | "version": "2.0.3", 1699 | "dev": true, 1700 | "license": "BSD-2-Clause", 1701 | "engines": { 1702 | "node": ">=0.10.0" 1703 | } 1704 | }, 1705 | "node_modules/events": { 1706 | "version": "3.3.0", 1707 | "dev": true, 1708 | "license": "MIT", 1709 | "engines": { 1710 | "node": ">=0.8.x" 1711 | } 1712 | }, 1713 | "node_modules/expand-template": { 1714 | "version": "2.0.3", 1715 | "dev": true, 1716 | "license": "(MIT OR WTFPL)", 1717 | "optional": true, 1718 | "engines": { 1719 | "node": ">=6" 1720 | } 1721 | }, 1722 | "node_modules/fast-deep-equal": { 1723 | "version": "3.1.3", 1724 | "dev": true, 1725 | "license": "MIT" 1726 | }, 1727 | "node_modules/fast-glob": { 1728 | "version": "3.3.3", 1729 | "dev": true, 1730 | "license": "MIT", 1731 | "dependencies": { 1732 | "@nodelib/fs.stat": "^2.0.2", 1733 | "@nodelib/fs.walk": "^1.2.3", 1734 | "glob-parent": "^5.1.2", 1735 | "merge2": "^1.3.0", 1736 | "micromatch": "^4.0.8" 1737 | }, 1738 | "engines": { 1739 | "node": ">=8.6.0" 1740 | } 1741 | }, 1742 | "node_modules/fast-json-stable-stringify": { 1743 | "version": "2.1.0", 1744 | "dev": true, 1745 | "license": "MIT" 1746 | }, 1747 | "node_modules/fast-levenshtein": { 1748 | "version": "2.0.6", 1749 | "dev": true, 1750 | "license": "MIT" 1751 | }, 1752 | "node_modules/fastq": { 1753 | "version": "1.19.1", 1754 | "dev": true, 1755 | "license": "ISC", 1756 | "dependencies": { 1757 | "reusify": "^1.0.4" 1758 | } 1759 | }, 1760 | "node_modules/fd-slicer": { 1761 | "version": "1.1.0", 1762 | "dev": true, 1763 | "license": "MIT", 1764 | "dependencies": { 1765 | "pend": "~1.2.0" 1766 | } 1767 | }, 1768 | "node_modules/file-entry-cache": { 1769 | "version": "8.0.0", 1770 | "dev": true, 1771 | "license": "MIT", 1772 | "dependencies": { 1773 | "flat-cache": "^4.0.0" 1774 | }, 1775 | "engines": { 1776 | "node": ">=16.0.0" 1777 | } 1778 | }, 1779 | "node_modules/fill-range": { 1780 | "version": "7.1.1", 1781 | "dev": true, 1782 | "license": "MIT", 1783 | "dependencies": { 1784 | "to-regex-range": "^5.0.1" 1785 | }, 1786 | "engines": { 1787 | "node": ">=8" 1788 | } 1789 | }, 1790 | "node_modules/find-up": { 1791 | "version": "5.0.0", 1792 | "dev": true, 1793 | "license": "MIT", 1794 | "dependencies": { 1795 | "locate-path": "^6.0.0", 1796 | "path-exists": "^4.0.0" 1797 | }, 1798 | "engines": { 1799 | "node": ">=10" 1800 | }, 1801 | "funding": { 1802 | "url": "https://github.com/sponsors/sindresorhus" 1803 | } 1804 | }, 1805 | "node_modules/flat-cache": { 1806 | "version": "4.0.1", 1807 | "dev": true, 1808 | "license": "MIT", 1809 | "dependencies": { 1810 | "flatted": "^3.2.9", 1811 | "keyv": "^4.5.4" 1812 | }, 1813 | "engines": { 1814 | "node": ">=16" 1815 | } 1816 | }, 1817 | "node_modules/flatted": { 1818 | "version": "3.3.3", 1819 | "dev": true, 1820 | "license": "ISC" 1821 | }, 1822 | "node_modules/foreground-child": { 1823 | "version": "3.3.1", 1824 | "dev": true, 1825 | "license": "ISC", 1826 | "dependencies": { 1827 | "cross-spawn": "^7.0.6", 1828 | "signal-exit": "^4.0.1" 1829 | }, 1830 | "engines": { 1831 | "node": ">=14" 1832 | }, 1833 | "funding": { 1834 | "url": "https://github.com/sponsors/isaacs" 1835 | } 1836 | }, 1837 | "node_modules/form-data": { 1838 | "version": "4.0.2", 1839 | "dev": true, 1840 | "license": "MIT", 1841 | "dependencies": { 1842 | "asynckit": "^0.4.0", 1843 | "combined-stream": "^1.0.8", 1844 | "es-set-tostringtag": "^2.1.0", 1845 | "mime-types": "^2.1.12" 1846 | }, 1847 | "engines": { 1848 | "node": ">= 6" 1849 | } 1850 | }, 1851 | "node_modules/fs-constants": { 1852 | "version": "1.0.0", 1853 | "dev": true, 1854 | "license": "MIT", 1855 | "optional": true 1856 | }, 1857 | "node_modules/function-bind": { 1858 | "version": "1.1.2", 1859 | "dev": true, 1860 | "license": "MIT", 1861 | "funding": { 1862 | "url": "https://github.com/sponsors/ljharb" 1863 | } 1864 | }, 1865 | "node_modules/get-intrinsic": { 1866 | "version": "1.3.0", 1867 | "dev": true, 1868 | "license": "MIT", 1869 | "dependencies": { 1870 | "call-bind-apply-helpers": "^1.0.2", 1871 | "es-define-property": "^1.0.1", 1872 | "es-errors": "^1.3.0", 1873 | "es-object-atoms": "^1.1.1", 1874 | "function-bind": "^1.1.2", 1875 | "get-proto": "^1.0.1", 1876 | "gopd": "^1.2.0", 1877 | "has-symbols": "^1.1.0", 1878 | "hasown": "^2.0.2", 1879 | "math-intrinsics": "^1.1.0" 1880 | }, 1881 | "engines": { 1882 | "node": ">= 0.4" 1883 | }, 1884 | "funding": { 1885 | "url": "https://github.com/sponsors/ljharb" 1886 | } 1887 | }, 1888 | "node_modules/get-proto": { 1889 | "version": "1.0.1", 1890 | "dev": true, 1891 | "license": "MIT", 1892 | "dependencies": { 1893 | "dunder-proto": "^1.0.1", 1894 | "es-object-atoms": "^1.0.0" 1895 | }, 1896 | "engines": { 1897 | "node": ">= 0.4" 1898 | } 1899 | }, 1900 | "node_modules/github-from-package": { 1901 | "version": "0.0.0", 1902 | "dev": true, 1903 | "license": "MIT", 1904 | "optional": true 1905 | }, 1906 | "node_modules/glob": { 1907 | "version": "11.0.1", 1908 | "dev": true, 1909 | "license": "ISC", 1910 | "dependencies": { 1911 | "foreground-child": "^3.1.0", 1912 | "jackspeak": "^4.0.1", 1913 | "minimatch": "^10.0.0", 1914 | "minipass": "^7.1.2", 1915 | "package-json-from-dist": "^1.0.0", 1916 | "path-scurry": "^2.0.0" 1917 | }, 1918 | "bin": { 1919 | "glob": "dist/esm/bin.mjs" 1920 | }, 1921 | "engines": { 1922 | "node": "20 || >=22" 1923 | }, 1924 | "funding": { 1925 | "url": "https://github.com/sponsors/isaacs" 1926 | } 1927 | }, 1928 | "node_modules/glob-parent": { 1929 | "version": "5.1.2", 1930 | "dev": true, 1931 | "license": "ISC", 1932 | "dependencies": { 1933 | "is-glob": "^4.0.1" 1934 | }, 1935 | "engines": { 1936 | "node": ">= 6" 1937 | } 1938 | }, 1939 | "node_modules/glob/node_modules/minimatch": { 1940 | "version": "10.0.1", 1941 | "dev": true, 1942 | "license": "ISC", 1943 | "dependencies": { 1944 | "brace-expansion": "^2.0.1" 1945 | }, 1946 | "engines": { 1947 | "node": "20 || >=22" 1948 | }, 1949 | "funding": { 1950 | "url": "https://github.com/sponsors/isaacs" 1951 | } 1952 | }, 1953 | "node_modules/globals": { 1954 | "version": "14.0.0", 1955 | "dev": true, 1956 | "license": "MIT", 1957 | "engines": { 1958 | "node": ">=18" 1959 | }, 1960 | "funding": { 1961 | "url": "https://github.com/sponsors/sindresorhus" 1962 | } 1963 | }, 1964 | "node_modules/gopd": { 1965 | "version": "1.2.0", 1966 | "dev": true, 1967 | "license": "MIT", 1968 | "engines": { 1969 | "node": ">= 0.4" 1970 | }, 1971 | "funding": { 1972 | "url": "https://github.com/sponsors/ljharb" 1973 | } 1974 | }, 1975 | "node_modules/graphemer": { 1976 | "version": "1.4.0", 1977 | "dev": true, 1978 | "license": "MIT" 1979 | }, 1980 | "node_modules/has-flag": { 1981 | "version": "4.0.0", 1982 | "dev": true, 1983 | "license": "MIT", 1984 | "engines": { 1985 | "node": ">=8" 1986 | } 1987 | }, 1988 | "node_modules/has-symbols": { 1989 | "version": "1.1.0", 1990 | "dev": true, 1991 | "license": "MIT", 1992 | "engines": { 1993 | "node": ">= 0.4" 1994 | }, 1995 | "funding": { 1996 | "url": "https://github.com/sponsors/ljharb" 1997 | } 1998 | }, 1999 | "node_modules/has-tostringtag": { 2000 | "version": "1.0.2", 2001 | "dev": true, 2002 | "license": "MIT", 2003 | "dependencies": { 2004 | "has-symbols": "^1.0.3" 2005 | }, 2006 | "engines": { 2007 | "node": ">= 0.4" 2008 | }, 2009 | "funding": { 2010 | "url": "https://github.com/sponsors/ljharb" 2011 | } 2012 | }, 2013 | "node_modules/hasown": { 2014 | "version": "2.0.2", 2015 | "dev": true, 2016 | "license": "MIT", 2017 | "dependencies": { 2018 | "function-bind": "^1.1.2" 2019 | }, 2020 | "engines": { 2021 | "node": ">= 0.4" 2022 | } 2023 | }, 2024 | "node_modules/hosted-git-info": { 2025 | "version": "4.1.0", 2026 | "dev": true, 2027 | "license": "ISC", 2028 | "dependencies": { 2029 | "lru-cache": "^6.0.0" 2030 | }, 2031 | "engines": { 2032 | "node": ">=10" 2033 | } 2034 | }, 2035 | "node_modules/htmlparser2": { 2036 | "version": "9.1.0", 2037 | "dev": true, 2038 | "funding": [ 2039 | "https://github.com/fb55/htmlparser2?sponsor=1", 2040 | { 2041 | "type": "github", 2042 | "url": "https://github.com/sponsors/fb55" 2043 | } 2044 | ], 2045 | "license": "MIT", 2046 | "dependencies": { 2047 | "domelementtype": "^2.3.0", 2048 | "domhandler": "^5.0.3", 2049 | "domutils": "^3.1.0", 2050 | "entities": "^4.5.0" 2051 | } 2052 | }, 2053 | "node_modules/http-proxy-agent": { 2054 | "version": "7.0.2", 2055 | "dev": true, 2056 | "license": "MIT", 2057 | "dependencies": { 2058 | "agent-base": "^7.1.0", 2059 | "debug": "^4.3.4" 2060 | }, 2061 | "engines": { 2062 | "node": ">= 14" 2063 | } 2064 | }, 2065 | "node_modules/https-proxy-agent": { 2066 | "version": "7.0.6", 2067 | "dev": true, 2068 | "license": "MIT", 2069 | "dependencies": { 2070 | "agent-base": "^7.1.2", 2071 | "debug": "4" 2072 | }, 2073 | "engines": { 2074 | "node": ">= 14" 2075 | } 2076 | }, 2077 | "node_modules/iconv-lite": { 2078 | "version": "0.6.3", 2079 | "dev": true, 2080 | "license": "MIT", 2081 | "dependencies": { 2082 | "safer-buffer": ">= 2.1.2 < 3.0.0" 2083 | }, 2084 | "engines": { 2085 | "node": ">=0.10.0" 2086 | } 2087 | }, 2088 | "node_modules/ieee754": { 2089 | "version": "1.2.1", 2090 | "dev": true, 2091 | "funding": [ 2092 | { 2093 | "type": "github", 2094 | "url": "https://github.com/sponsors/feross" 2095 | }, 2096 | { 2097 | "type": "patreon", 2098 | "url": "https://www.patreon.com/feross" 2099 | }, 2100 | { 2101 | "type": "consulting", 2102 | "url": "https://feross.org/support" 2103 | } 2104 | ], 2105 | "license": "BSD-3-Clause", 2106 | "optional": true 2107 | }, 2108 | "node_modules/ignore": { 2109 | "version": "5.3.2", 2110 | "dev": true, 2111 | "license": "MIT", 2112 | "engines": { 2113 | "node": ">= 4" 2114 | } 2115 | }, 2116 | "node_modules/import-fresh": { 2117 | "version": "3.3.1", 2118 | "dev": true, 2119 | "license": "MIT", 2120 | "dependencies": { 2121 | "parent-module": "^1.0.0", 2122 | "resolve-from": "^4.0.0" 2123 | }, 2124 | "engines": { 2125 | "node": ">=6" 2126 | }, 2127 | "funding": { 2128 | "url": "https://github.com/sponsors/sindresorhus" 2129 | } 2130 | }, 2131 | "node_modules/imurmurhash": { 2132 | "version": "0.1.4", 2133 | "dev": true, 2134 | "license": "MIT", 2135 | "engines": { 2136 | "node": ">=0.8.19" 2137 | } 2138 | }, 2139 | "node_modules/inherits": { 2140 | "version": "2.0.4", 2141 | "dev": true, 2142 | "license": "ISC", 2143 | "optional": true 2144 | }, 2145 | "node_modules/ini": { 2146 | "version": "1.3.8", 2147 | "dev": true, 2148 | "license": "ISC", 2149 | "optional": true 2150 | }, 2151 | "node_modules/is-docker": { 2152 | "version": "3.0.0", 2153 | "dev": true, 2154 | "license": "MIT", 2155 | "bin": { 2156 | "is-docker": "cli.js" 2157 | }, 2158 | "engines": { 2159 | "node": "^12.20.0 || ^14.13.1 || >=16.0.0" 2160 | }, 2161 | "funding": { 2162 | "url": "https://github.com/sponsors/sindresorhus" 2163 | } 2164 | }, 2165 | "node_modules/is-extglob": { 2166 | "version": "2.1.1", 2167 | "dev": true, 2168 | "license": "MIT", 2169 | "engines": { 2170 | "node": ">=0.10.0" 2171 | } 2172 | }, 2173 | "node_modules/is-fullwidth-code-point": { 2174 | "version": "3.0.0", 2175 | "dev": true, 2176 | "license": "MIT", 2177 | "engines": { 2178 | "node": ">=8" 2179 | } 2180 | }, 2181 | "node_modules/is-glob": { 2182 | "version": "4.0.3", 2183 | "dev": true, 2184 | "license": "MIT", 2185 | "dependencies": { 2186 | "is-extglob": "^2.1.1" 2187 | }, 2188 | "engines": { 2189 | "node": ">=0.10.0" 2190 | } 2191 | }, 2192 | "node_modules/is-inside-container": { 2193 | "version": "1.0.0", 2194 | "dev": true, 2195 | "license": "MIT", 2196 | "dependencies": { 2197 | "is-docker": "^3.0.0" 2198 | }, 2199 | "bin": { 2200 | "is-inside-container": "cli.js" 2201 | }, 2202 | "engines": { 2203 | "node": ">=14.16" 2204 | }, 2205 | "funding": { 2206 | "url": "https://github.com/sponsors/sindresorhus" 2207 | } 2208 | }, 2209 | "node_modules/is-number": { 2210 | "version": "7.0.0", 2211 | "dev": true, 2212 | "license": "MIT", 2213 | "engines": { 2214 | "node": ">=0.12.0" 2215 | } 2216 | }, 2217 | "node_modules/is-wsl": { 2218 | "version": "3.1.0", 2219 | "dev": true, 2220 | "license": "MIT", 2221 | "dependencies": { 2222 | "is-inside-container": "^1.0.0" 2223 | }, 2224 | "engines": { 2225 | "node": ">=16" 2226 | }, 2227 | "funding": { 2228 | "url": "https://github.com/sponsors/sindresorhus" 2229 | } 2230 | }, 2231 | "node_modules/isexe": { 2232 | "version": "2.0.0", 2233 | "dev": true, 2234 | "license": "ISC" 2235 | }, 2236 | "node_modules/jackspeak": { 2237 | "version": "4.1.0", 2238 | "dev": true, 2239 | "license": "BlueOak-1.0.0", 2240 | "dependencies": { 2241 | "@isaacs/cliui": "^8.0.2" 2242 | }, 2243 | "engines": { 2244 | "node": "20 || >=22" 2245 | }, 2246 | "funding": { 2247 | "url": "https://github.com/sponsors/isaacs" 2248 | } 2249 | }, 2250 | "node_modules/js-yaml": { 2251 | "version": "4.1.0", 2252 | "dev": true, 2253 | "license": "MIT", 2254 | "dependencies": { 2255 | "argparse": "^2.0.1" 2256 | }, 2257 | "bin": { 2258 | "js-yaml": "bin/js-yaml.js" 2259 | } 2260 | }, 2261 | "node_modules/json-buffer": { 2262 | "version": "3.0.1", 2263 | "dev": true, 2264 | "license": "MIT" 2265 | }, 2266 | "node_modules/json-schema-traverse": { 2267 | "version": "0.4.1", 2268 | "dev": true, 2269 | "license": "MIT" 2270 | }, 2271 | "node_modules/json-stable-stringify-without-jsonify": { 2272 | "version": "1.0.1", 2273 | "dev": true, 2274 | "license": "MIT" 2275 | }, 2276 | "node_modules/jsonc-parser": { 2277 | "version": "3.3.1", 2278 | "dev": true, 2279 | "license": "MIT" 2280 | }, 2281 | "node_modules/jsonwebtoken": { 2282 | "version": "9.0.2", 2283 | "dev": true, 2284 | "license": "MIT", 2285 | "dependencies": { 2286 | "jws": "^3.2.2", 2287 | "lodash.includes": "^4.3.0", 2288 | "lodash.isboolean": "^3.0.3", 2289 | "lodash.isinteger": "^4.0.4", 2290 | "lodash.isnumber": "^3.0.3", 2291 | "lodash.isplainobject": "^4.0.6", 2292 | "lodash.isstring": "^4.0.1", 2293 | "lodash.once": "^4.0.0", 2294 | "ms": "^2.1.1", 2295 | "semver": "^7.5.4" 2296 | }, 2297 | "engines": { 2298 | "node": ">=12", 2299 | "npm": ">=6" 2300 | } 2301 | }, 2302 | "node_modules/jsonwebtoken/node_modules/jwa": { 2303 | "version": "1.4.1", 2304 | "dev": true, 2305 | "license": "MIT", 2306 | "dependencies": { 2307 | "buffer-equal-constant-time": "1.0.1", 2308 | "ecdsa-sig-formatter": "1.0.11", 2309 | "safe-buffer": "^5.0.1" 2310 | } 2311 | }, 2312 | "node_modules/jsonwebtoken/node_modules/jws": { 2313 | "version": "3.2.2", 2314 | "dev": true, 2315 | "license": "MIT", 2316 | "dependencies": { 2317 | "jwa": "^1.4.1", 2318 | "safe-buffer": "^5.0.1" 2319 | } 2320 | }, 2321 | "node_modules/jwa": { 2322 | "version": "2.0.0", 2323 | "dev": true, 2324 | "license": "MIT", 2325 | "dependencies": { 2326 | "buffer-equal-constant-time": "1.0.1", 2327 | "ecdsa-sig-formatter": "1.0.11", 2328 | "safe-buffer": "^5.0.1" 2329 | } 2330 | }, 2331 | "node_modules/jws": { 2332 | "version": "4.0.0", 2333 | "dev": true, 2334 | "license": "MIT", 2335 | "dependencies": { 2336 | "jwa": "^2.0.0", 2337 | "safe-buffer": "^5.0.1" 2338 | } 2339 | }, 2340 | "node_modules/keytar": { 2341 | "version": "7.9.0", 2342 | "dev": true, 2343 | "hasInstallScript": true, 2344 | "license": "MIT", 2345 | "optional": true, 2346 | "dependencies": { 2347 | "node-addon-api": "^4.3.0", 2348 | "prebuild-install": "^7.0.1" 2349 | } 2350 | }, 2351 | "node_modules/keyv": { 2352 | "version": "4.5.4", 2353 | "dev": true, 2354 | "license": "MIT", 2355 | "dependencies": { 2356 | "json-buffer": "3.0.1" 2357 | } 2358 | }, 2359 | "node_modules/leven": { 2360 | "version": "3.1.0", 2361 | "dev": true, 2362 | "license": "MIT", 2363 | "engines": { 2364 | "node": ">=6" 2365 | } 2366 | }, 2367 | "node_modules/levn": { 2368 | "version": "0.4.1", 2369 | "dev": true, 2370 | "license": "MIT", 2371 | "dependencies": { 2372 | "prelude-ls": "^1.2.1", 2373 | "type-check": "~0.4.0" 2374 | }, 2375 | "engines": { 2376 | "node": ">= 0.8.0" 2377 | } 2378 | }, 2379 | "node_modules/linkify-it": { 2380 | "version": "5.0.0", 2381 | "dev": true, 2382 | "license": "MIT", 2383 | "dependencies": { 2384 | "uc.micro": "^2.0.0" 2385 | } 2386 | }, 2387 | "node_modules/locate-path": { 2388 | "version": "6.0.0", 2389 | "dev": true, 2390 | "license": "MIT", 2391 | "dependencies": { 2392 | "p-locate": "^5.0.0" 2393 | }, 2394 | "engines": { 2395 | "node": ">=10" 2396 | }, 2397 | "funding": { 2398 | "url": "https://github.com/sponsors/sindresorhus" 2399 | } 2400 | }, 2401 | "node_modules/lodash.includes": { 2402 | "version": "4.3.0", 2403 | "dev": true, 2404 | "license": "MIT" 2405 | }, 2406 | "node_modules/lodash.isboolean": { 2407 | "version": "3.0.3", 2408 | "dev": true, 2409 | "license": "MIT" 2410 | }, 2411 | "node_modules/lodash.isinteger": { 2412 | "version": "4.0.4", 2413 | "dev": true, 2414 | "license": "MIT" 2415 | }, 2416 | "node_modules/lodash.isnumber": { 2417 | "version": "3.0.3", 2418 | "dev": true, 2419 | "license": "MIT" 2420 | }, 2421 | "node_modules/lodash.isplainobject": { 2422 | "version": "4.0.6", 2423 | "dev": true, 2424 | "license": "MIT" 2425 | }, 2426 | "node_modules/lodash.isstring": { 2427 | "version": "4.0.1", 2428 | "dev": true, 2429 | "license": "MIT" 2430 | }, 2431 | "node_modules/lodash.merge": { 2432 | "version": "4.6.2", 2433 | "dev": true, 2434 | "license": "MIT" 2435 | }, 2436 | "node_modules/lodash.once": { 2437 | "version": "4.1.1", 2438 | "dev": true, 2439 | "license": "MIT" 2440 | }, 2441 | "node_modules/lru-cache": { 2442 | "version": "6.0.0", 2443 | "dev": true, 2444 | "license": "ISC", 2445 | "dependencies": { 2446 | "yallist": "^4.0.0" 2447 | }, 2448 | "engines": { 2449 | "node": ">=10" 2450 | } 2451 | }, 2452 | "node_modules/markdown-it": { 2453 | "version": "14.1.0", 2454 | "dev": true, 2455 | "license": "MIT", 2456 | "dependencies": { 2457 | "argparse": "^2.0.1", 2458 | "entities": "^4.4.0", 2459 | "linkify-it": "^5.0.0", 2460 | "mdurl": "^2.0.0", 2461 | "punycode.js": "^2.3.1", 2462 | "uc.micro": "^2.1.0" 2463 | }, 2464 | "bin": { 2465 | "markdown-it": "bin/markdown-it.mjs" 2466 | } 2467 | }, 2468 | "node_modules/math-intrinsics": { 2469 | "version": "1.1.0", 2470 | "dev": true, 2471 | "license": "MIT", 2472 | "engines": { 2473 | "node": ">= 0.4" 2474 | } 2475 | }, 2476 | "node_modules/mdurl": { 2477 | "version": "2.0.0", 2478 | "dev": true, 2479 | "license": "MIT" 2480 | }, 2481 | "node_modules/merge2": { 2482 | "version": "1.4.1", 2483 | "dev": true, 2484 | "license": "MIT", 2485 | "engines": { 2486 | "node": ">= 8" 2487 | } 2488 | }, 2489 | "node_modules/micromatch": { 2490 | "version": "4.0.8", 2491 | "dev": true, 2492 | "license": "MIT", 2493 | "dependencies": { 2494 | "braces": "^3.0.3", 2495 | "picomatch": "^2.3.1" 2496 | }, 2497 | "engines": { 2498 | "node": ">=8.6" 2499 | } 2500 | }, 2501 | "node_modules/mime-db": { 2502 | "version": "1.52.0", 2503 | "dev": true, 2504 | "license": "MIT", 2505 | "engines": { 2506 | "node": ">= 0.6" 2507 | } 2508 | }, 2509 | "node_modules/mime-types": { 2510 | "version": "2.1.35", 2511 | "dev": true, 2512 | "license": "MIT", 2513 | "dependencies": { 2514 | "mime-db": "1.52.0" 2515 | }, 2516 | "engines": { 2517 | "node": ">= 0.6" 2518 | } 2519 | }, 2520 | "node_modules/mimic-response": { 2521 | "version": "3.1.0", 2522 | "dev": true, 2523 | "license": "MIT", 2524 | "optional": true, 2525 | "engines": { 2526 | "node": ">=10" 2527 | }, 2528 | "funding": { 2529 | "url": "https://github.com/sponsors/sindresorhus" 2530 | } 2531 | }, 2532 | "node_modules/minimatch": { 2533 | "version": "9.0.5", 2534 | "dev": true, 2535 | "license": "ISC", 2536 | "dependencies": { 2537 | "brace-expansion": "^2.0.1" 2538 | }, 2539 | "engines": { 2540 | "node": ">=16 || 14 >=14.17" 2541 | }, 2542 | "funding": { 2543 | "url": "https://github.com/sponsors/isaacs" 2544 | } 2545 | }, 2546 | "node_modules/minimist": { 2547 | "version": "1.2.8", 2548 | "dev": true, 2549 | "license": "MIT", 2550 | "optional": true, 2551 | "funding": { 2552 | "url": "https://github.com/sponsors/ljharb" 2553 | } 2554 | }, 2555 | "node_modules/minipass": { 2556 | "version": "7.1.2", 2557 | "dev": true, 2558 | "license": "ISC", 2559 | "engines": { 2560 | "node": ">=16 || 14 >=14.17" 2561 | } 2562 | }, 2563 | "node_modules/mkdirp-classic": { 2564 | "version": "0.5.3", 2565 | "dev": true, 2566 | "license": "MIT", 2567 | "optional": true 2568 | }, 2569 | "node_modules/ms": { 2570 | "version": "2.1.3", 2571 | "dev": true, 2572 | "license": "MIT" 2573 | }, 2574 | "node_modules/mute-stream": { 2575 | "version": "0.0.8", 2576 | "dev": true, 2577 | "license": "ISC" 2578 | }, 2579 | "node_modules/napi-build-utils": { 2580 | "version": "2.0.0", 2581 | "dev": true, 2582 | "license": "MIT", 2583 | "optional": true 2584 | }, 2585 | "node_modules/natural-compare": { 2586 | "version": "1.4.0", 2587 | "dev": true, 2588 | "license": "MIT" 2589 | }, 2590 | "node_modules/node-abi": { 2591 | "version": "3.74.0", 2592 | "dev": true, 2593 | "license": "MIT", 2594 | "optional": true, 2595 | "dependencies": { 2596 | "semver": "^7.3.5" 2597 | }, 2598 | "engines": { 2599 | "node": ">=10" 2600 | } 2601 | }, 2602 | "node_modules/node-addon-api": { 2603 | "version": "4.3.0", 2604 | "dev": true, 2605 | "license": "MIT", 2606 | "optional": true 2607 | }, 2608 | "node_modules/nth-check": { 2609 | "version": "2.1.1", 2610 | "dev": true, 2611 | "license": "BSD-2-Clause", 2612 | "dependencies": { 2613 | "boolbase": "^1.0.0" 2614 | }, 2615 | "funding": { 2616 | "url": "https://github.com/fb55/nth-check?sponsor=1" 2617 | } 2618 | }, 2619 | "node_modules/object-inspect": { 2620 | "version": "1.13.4", 2621 | "dev": true, 2622 | "license": "MIT", 2623 | "engines": { 2624 | "node": ">= 0.4" 2625 | }, 2626 | "funding": { 2627 | "url": "https://github.com/sponsors/ljharb" 2628 | } 2629 | }, 2630 | "node_modules/once": { 2631 | "version": "1.4.0", 2632 | "dev": true, 2633 | "license": "ISC", 2634 | "optional": true, 2635 | "dependencies": { 2636 | "wrappy": "1" 2637 | } 2638 | }, 2639 | "node_modules/open": { 2640 | "version": "10.1.0", 2641 | "dev": true, 2642 | "license": "MIT", 2643 | "dependencies": { 2644 | "default-browser": "^5.2.1", 2645 | "define-lazy-prop": "^3.0.0", 2646 | "is-inside-container": "^1.0.0", 2647 | "is-wsl": "^3.1.0" 2648 | }, 2649 | "engines": { 2650 | "node": ">=18" 2651 | }, 2652 | "funding": { 2653 | "url": "https://github.com/sponsors/sindresorhus" 2654 | } 2655 | }, 2656 | "node_modules/optionator": { 2657 | "version": "0.9.4", 2658 | "dev": true, 2659 | "license": "MIT", 2660 | "dependencies": { 2661 | "deep-is": "^0.1.3", 2662 | "fast-levenshtein": "^2.0.6", 2663 | "levn": "^0.4.1", 2664 | "prelude-ls": "^1.2.1", 2665 | "type-check": "^0.4.0", 2666 | "word-wrap": "^1.2.5" 2667 | }, 2668 | "engines": { 2669 | "node": ">= 0.8.0" 2670 | } 2671 | }, 2672 | "node_modules/p-limit": { 2673 | "version": "3.1.0", 2674 | "dev": true, 2675 | "license": "MIT", 2676 | "dependencies": { 2677 | "yocto-queue": "^0.1.0" 2678 | }, 2679 | "engines": { 2680 | "node": ">=10" 2681 | }, 2682 | "funding": { 2683 | "url": "https://github.com/sponsors/sindresorhus" 2684 | } 2685 | }, 2686 | "node_modules/p-locate": { 2687 | "version": "5.0.0", 2688 | "dev": true, 2689 | "license": "MIT", 2690 | "dependencies": { 2691 | "p-limit": "^3.0.2" 2692 | }, 2693 | "engines": { 2694 | "node": ">=10" 2695 | }, 2696 | "funding": { 2697 | "url": "https://github.com/sponsors/sindresorhus" 2698 | } 2699 | }, 2700 | "node_modules/package-json-from-dist": { 2701 | "version": "1.0.1", 2702 | "dev": true, 2703 | "license": "BlueOak-1.0.0" 2704 | }, 2705 | "node_modules/parent-module": { 2706 | "version": "1.0.1", 2707 | "dev": true, 2708 | "license": "MIT", 2709 | "dependencies": { 2710 | "callsites": "^3.0.0" 2711 | }, 2712 | "engines": { 2713 | "node": ">=6" 2714 | } 2715 | }, 2716 | "node_modules/parse-semver": { 2717 | "version": "1.1.1", 2718 | "dev": true, 2719 | "license": "MIT", 2720 | "dependencies": { 2721 | "semver": "^5.1.0" 2722 | } 2723 | }, 2724 | "node_modules/parse-semver/node_modules/semver": { 2725 | "version": "5.7.2", 2726 | "dev": true, 2727 | "license": "ISC", 2728 | "bin": { 2729 | "semver": "bin/semver" 2730 | } 2731 | }, 2732 | "node_modules/parse5-parser-stream": { 2733 | "version": "7.1.2", 2734 | "dev": true, 2735 | "license": "MIT", 2736 | "dependencies": { 2737 | "parse5": "^7.0.0" 2738 | }, 2739 | "funding": { 2740 | "url": "https://github.com/inikulin/parse5?sponsor=1" 2741 | } 2742 | }, 2743 | "node_modules/parse5-parser-stream/node_modules/parse5": { 2744 | "version": "7.2.1", 2745 | "dev": true, 2746 | "license": "MIT", 2747 | "dependencies": { 2748 | "entities": "^4.5.0" 2749 | }, 2750 | "funding": { 2751 | "url": "https://github.com/inikulin/parse5?sponsor=1" 2752 | } 2753 | }, 2754 | "node_modules/path-exists": { 2755 | "version": "4.0.0", 2756 | "dev": true, 2757 | "license": "MIT", 2758 | "engines": { 2759 | "node": ">=8" 2760 | } 2761 | }, 2762 | "node_modules/path-key": { 2763 | "version": "3.1.1", 2764 | "dev": true, 2765 | "license": "MIT", 2766 | "engines": { 2767 | "node": ">=8" 2768 | } 2769 | }, 2770 | "node_modules/path-scurry": { 2771 | "version": "2.0.0", 2772 | "dev": true, 2773 | "license": "BlueOak-1.0.0", 2774 | "dependencies": { 2775 | "lru-cache": "^11.0.0", 2776 | "minipass": "^7.1.2" 2777 | }, 2778 | "engines": { 2779 | "node": "20 || >=22" 2780 | }, 2781 | "funding": { 2782 | "url": "https://github.com/sponsors/isaacs" 2783 | } 2784 | }, 2785 | "node_modules/path-scurry/node_modules/lru-cache": { 2786 | "version": "11.0.2", 2787 | "dev": true, 2788 | "license": "ISC", 2789 | "engines": { 2790 | "node": "20 || >=22" 2791 | } 2792 | }, 2793 | "node_modules/pend": { 2794 | "version": "1.2.0", 2795 | "dev": true, 2796 | "license": "MIT" 2797 | }, 2798 | "node_modules/picomatch": { 2799 | "version": "2.3.1", 2800 | "dev": true, 2801 | "license": "MIT", 2802 | "engines": { 2803 | "node": ">=8.6" 2804 | }, 2805 | "funding": { 2806 | "url": "https://github.com/sponsors/jonschlinkert" 2807 | } 2808 | }, 2809 | "node_modules/prebuild-install": { 2810 | "version": "7.1.3", 2811 | "dev": true, 2812 | "license": "MIT", 2813 | "optional": true, 2814 | "dependencies": { 2815 | "detect-libc": "^2.0.0", 2816 | "expand-template": "^2.0.3", 2817 | "github-from-package": "0.0.0", 2818 | "minimist": "^1.2.3", 2819 | "mkdirp-classic": "^0.5.3", 2820 | "napi-build-utils": "^2.0.0", 2821 | "node-abi": "^3.3.0", 2822 | "pump": "^3.0.0", 2823 | "rc": "^1.2.7", 2824 | "simple-get": "^4.0.0", 2825 | "tar-fs": "^2.0.0", 2826 | "tunnel-agent": "^0.6.0" 2827 | }, 2828 | "bin": { 2829 | "prebuild-install": "bin.js" 2830 | }, 2831 | "engines": { 2832 | "node": ">=10" 2833 | } 2834 | }, 2835 | "node_modules/prelude-ls": { 2836 | "version": "1.2.1", 2837 | "dev": true, 2838 | "license": "MIT", 2839 | "engines": { 2840 | "node": ">= 0.8.0" 2841 | } 2842 | }, 2843 | "node_modules/pump": { 2844 | "version": "3.0.2", 2845 | "dev": true, 2846 | "license": "MIT", 2847 | "optional": true, 2848 | "dependencies": { 2849 | "end-of-stream": "^1.1.0", 2850 | "once": "^1.3.1" 2851 | } 2852 | }, 2853 | "node_modules/punycode": { 2854 | "version": "2.3.1", 2855 | "dev": true, 2856 | "license": "MIT", 2857 | "engines": { 2858 | "node": ">=6" 2859 | } 2860 | }, 2861 | "node_modules/punycode.js": { 2862 | "version": "2.3.1", 2863 | "dev": true, 2864 | "license": "MIT", 2865 | "engines": { 2866 | "node": ">=6" 2867 | } 2868 | }, 2869 | "node_modules/qs": { 2870 | "version": "6.14.0", 2871 | "dev": true, 2872 | "license": "BSD-3-Clause", 2873 | "dependencies": { 2874 | "side-channel": "^1.1.0" 2875 | }, 2876 | "engines": { 2877 | "node": ">=0.6" 2878 | }, 2879 | "funding": { 2880 | "url": "https://github.com/sponsors/ljharb" 2881 | } 2882 | }, 2883 | "node_modules/queue-microtask": { 2884 | "version": "1.2.3", 2885 | "dev": true, 2886 | "funding": [ 2887 | { 2888 | "type": "github", 2889 | "url": "https://github.com/sponsors/feross" 2890 | }, 2891 | { 2892 | "type": "patreon", 2893 | "url": "https://www.patreon.com/feross" 2894 | }, 2895 | { 2896 | "type": "consulting", 2897 | "url": "https://feross.org/support" 2898 | } 2899 | ], 2900 | "license": "MIT" 2901 | }, 2902 | "node_modules/rc": { 2903 | "version": "1.2.8", 2904 | "dev": true, 2905 | "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", 2906 | "optional": true, 2907 | "dependencies": { 2908 | "deep-extend": "^0.6.0", 2909 | "ini": "~1.3.0", 2910 | "minimist": "^1.2.0", 2911 | "strip-json-comments": "~2.0.1" 2912 | }, 2913 | "bin": { 2914 | "rc": "cli.js" 2915 | } 2916 | }, 2917 | "node_modules/rc/node_modules/strip-json-comments": { 2918 | "version": "2.0.1", 2919 | "dev": true, 2920 | "license": "MIT", 2921 | "optional": true, 2922 | "engines": { 2923 | "node": ">=0.10.0" 2924 | } 2925 | }, 2926 | "node_modules/read": { 2927 | "version": "1.0.7", 2928 | "dev": true, 2929 | "license": "ISC", 2930 | "dependencies": { 2931 | "mute-stream": "~0.0.4" 2932 | }, 2933 | "engines": { 2934 | "node": ">=0.8" 2935 | } 2936 | }, 2937 | "node_modules/readable-stream": { 2938 | "version": "3.6.2", 2939 | "dev": true, 2940 | "license": "MIT", 2941 | "optional": true, 2942 | "dependencies": { 2943 | "inherits": "^2.0.3", 2944 | "string_decoder": "^1.1.1", 2945 | "util-deprecate": "^1.0.1" 2946 | }, 2947 | "engines": { 2948 | "node": ">= 6" 2949 | } 2950 | }, 2951 | "node_modules/resolve-from": { 2952 | "version": "4.0.0", 2953 | "dev": true, 2954 | "license": "MIT", 2955 | "engines": { 2956 | "node": ">=4" 2957 | } 2958 | }, 2959 | "node_modules/reusify": { 2960 | "version": "1.1.0", 2961 | "dev": true, 2962 | "license": "MIT", 2963 | "engines": { 2964 | "iojs": ">=1.0.0", 2965 | "node": ">=0.10.0" 2966 | } 2967 | }, 2968 | "node_modules/run-applescript": { 2969 | "version": "7.0.0", 2970 | "dev": true, 2971 | "license": "MIT", 2972 | "engines": { 2973 | "node": ">=18" 2974 | }, 2975 | "funding": { 2976 | "url": "https://github.com/sponsors/sindresorhus" 2977 | } 2978 | }, 2979 | "node_modules/run-parallel": { 2980 | "version": "1.2.0", 2981 | "dev": true, 2982 | "funding": [ 2983 | { 2984 | "type": "github", 2985 | "url": "https://github.com/sponsors/feross" 2986 | }, 2987 | { 2988 | "type": "patreon", 2989 | "url": "https://www.patreon.com/feross" 2990 | }, 2991 | { 2992 | "type": "consulting", 2993 | "url": "https://feross.org/support" 2994 | } 2995 | ], 2996 | "license": "MIT", 2997 | "dependencies": { 2998 | "queue-microtask": "^1.2.2" 2999 | } 3000 | }, 3001 | "node_modules/safe-buffer": { 3002 | "version": "5.1.2", 3003 | "dev": true, 3004 | "license": "MIT" 3005 | }, 3006 | "node_modules/safer-buffer": { 3007 | "version": "2.1.2", 3008 | "dev": true, 3009 | "license": "MIT" 3010 | }, 3011 | "node_modules/sax": { 3012 | "version": "1.4.1", 3013 | "dev": true, 3014 | "license": "ISC" 3015 | }, 3016 | "node_modules/semver": { 3017 | "version": "7.7.1", 3018 | "license": "ISC", 3019 | "bin": { 3020 | "semver": "bin/semver.js" 3021 | }, 3022 | "engines": { 3023 | "node": ">=10" 3024 | } 3025 | }, 3026 | "node_modules/shebang-command": { 3027 | "version": "2.0.0", 3028 | "dev": true, 3029 | "license": "MIT", 3030 | "dependencies": { 3031 | "shebang-regex": "^3.0.0" 3032 | }, 3033 | "engines": { 3034 | "node": ">=8" 3035 | } 3036 | }, 3037 | "node_modules/shebang-regex": { 3038 | "version": "3.0.0", 3039 | "dev": true, 3040 | "license": "MIT", 3041 | "engines": { 3042 | "node": ">=8" 3043 | } 3044 | }, 3045 | "node_modules/side-channel": { 3046 | "version": "1.1.0", 3047 | "dev": true, 3048 | "license": "MIT", 3049 | "dependencies": { 3050 | "es-errors": "^1.3.0", 3051 | "object-inspect": "^1.13.3", 3052 | "side-channel-list": "^1.0.0", 3053 | "side-channel-map": "^1.0.1", 3054 | "side-channel-weakmap": "^1.0.2" 3055 | }, 3056 | "engines": { 3057 | "node": ">= 0.4" 3058 | }, 3059 | "funding": { 3060 | "url": "https://github.com/sponsors/ljharb" 3061 | } 3062 | }, 3063 | "node_modules/side-channel-list": { 3064 | "version": "1.0.0", 3065 | "dev": true, 3066 | "license": "MIT", 3067 | "dependencies": { 3068 | "es-errors": "^1.3.0", 3069 | "object-inspect": "^1.13.3" 3070 | }, 3071 | "engines": { 3072 | "node": ">= 0.4" 3073 | }, 3074 | "funding": { 3075 | "url": "https://github.com/sponsors/ljharb" 3076 | } 3077 | }, 3078 | "node_modules/side-channel-map": { 3079 | "version": "1.0.1", 3080 | "dev": true, 3081 | "license": "MIT", 3082 | "dependencies": { 3083 | "call-bound": "^1.0.2", 3084 | "es-errors": "^1.3.0", 3085 | "get-intrinsic": "^1.2.5", 3086 | "object-inspect": "^1.13.3" 3087 | }, 3088 | "engines": { 3089 | "node": ">= 0.4" 3090 | }, 3091 | "funding": { 3092 | "url": "https://github.com/sponsors/ljharb" 3093 | } 3094 | }, 3095 | "node_modules/side-channel-weakmap": { 3096 | "version": "1.0.2", 3097 | "dev": true, 3098 | "license": "MIT", 3099 | "dependencies": { 3100 | "call-bound": "^1.0.2", 3101 | "es-errors": "^1.3.0", 3102 | "get-intrinsic": "^1.2.5", 3103 | "object-inspect": "^1.13.3", 3104 | "side-channel-map": "^1.0.1" 3105 | }, 3106 | "engines": { 3107 | "node": ">= 0.4" 3108 | }, 3109 | "funding": { 3110 | "url": "https://github.com/sponsors/ljharb" 3111 | } 3112 | }, 3113 | "node_modules/signal-exit": { 3114 | "version": "4.1.0", 3115 | "dev": true, 3116 | "license": "ISC", 3117 | "engines": { 3118 | "node": ">=14" 3119 | }, 3120 | "funding": { 3121 | "url": "https://github.com/sponsors/isaacs" 3122 | } 3123 | }, 3124 | "node_modules/simple-concat": { 3125 | "version": "1.0.1", 3126 | "dev": true, 3127 | "funding": [ 3128 | { 3129 | "type": "github", 3130 | "url": "https://github.com/sponsors/feross" 3131 | }, 3132 | { 3133 | "type": "patreon", 3134 | "url": "https://www.patreon.com/feross" 3135 | }, 3136 | { 3137 | "type": "consulting", 3138 | "url": "https://feross.org/support" 3139 | } 3140 | ], 3141 | "license": "MIT", 3142 | "optional": true 3143 | }, 3144 | "node_modules/simple-get": { 3145 | "version": "4.0.1", 3146 | "dev": true, 3147 | "funding": [ 3148 | { 3149 | "type": "github", 3150 | "url": "https://github.com/sponsors/feross" 3151 | }, 3152 | { 3153 | "type": "patreon", 3154 | "url": "https://www.patreon.com/feross" 3155 | }, 3156 | { 3157 | "type": "consulting", 3158 | "url": "https://feross.org/support" 3159 | } 3160 | ], 3161 | "license": "MIT", 3162 | "optional": true, 3163 | "dependencies": { 3164 | "decompress-response": "^6.0.0", 3165 | "once": "^1.3.1", 3166 | "simple-concat": "^1.0.0" 3167 | } 3168 | }, 3169 | "node_modules/stoppable": { 3170 | "version": "1.1.0", 3171 | "dev": true, 3172 | "license": "MIT", 3173 | "engines": { 3174 | "node": ">=4", 3175 | "npm": ">=6" 3176 | } 3177 | }, 3178 | "node_modules/string_decoder": { 3179 | "version": "1.1.1", 3180 | "dev": true, 3181 | "license": "MIT", 3182 | "optional": true, 3183 | "dependencies": { 3184 | "safe-buffer": "~5.1.0" 3185 | } 3186 | }, 3187 | "node_modules/string-width": { 3188 | "version": "4.2.3", 3189 | "dev": true, 3190 | "license": "MIT", 3191 | "dependencies": { 3192 | "emoji-regex": "^8.0.0", 3193 | "is-fullwidth-code-point": "^3.0.0", 3194 | "strip-ansi": "^6.0.1" 3195 | }, 3196 | "engines": { 3197 | "node": ">=8" 3198 | } 3199 | }, 3200 | "node_modules/string-width-cjs": { 3201 | "name": "string-width", 3202 | "version": "4.2.3", 3203 | "dev": true, 3204 | "license": "MIT", 3205 | "dependencies": { 3206 | "emoji-regex": "^8.0.0", 3207 | "is-fullwidth-code-point": "^3.0.0", 3208 | "strip-ansi": "^6.0.1" 3209 | }, 3210 | "engines": { 3211 | "node": ">=8" 3212 | } 3213 | }, 3214 | "node_modules/strip-ansi": { 3215 | "version": "6.0.1", 3216 | "dev": true, 3217 | "license": "MIT", 3218 | "dependencies": { 3219 | "ansi-regex": "^5.0.1" 3220 | }, 3221 | "engines": { 3222 | "node": ">=8" 3223 | } 3224 | }, 3225 | "node_modules/strip-ansi-cjs": { 3226 | "name": "strip-ansi", 3227 | "version": "6.0.1", 3228 | "dev": true, 3229 | "license": "MIT", 3230 | "dependencies": { 3231 | "ansi-regex": "^5.0.1" 3232 | }, 3233 | "engines": { 3234 | "node": ">=8" 3235 | } 3236 | }, 3237 | "node_modules/strip-ansi-cjs/node_modules/ansi-regex": { 3238 | "version": "5.0.1", 3239 | "dev": true, 3240 | "license": "MIT", 3241 | "engines": { 3242 | "node": ">=8" 3243 | } 3244 | }, 3245 | "node_modules/strip-ansi/node_modules/ansi-regex": { 3246 | "version": "5.0.1", 3247 | "dev": true, 3248 | "license": "MIT", 3249 | "engines": { 3250 | "node": ">=8" 3251 | } 3252 | }, 3253 | "node_modules/strip-json-comments": { 3254 | "version": "3.1.1", 3255 | "dev": true, 3256 | "license": "MIT", 3257 | "engines": { 3258 | "node": ">=8" 3259 | }, 3260 | "funding": { 3261 | "url": "https://github.com/sponsors/sindresorhus" 3262 | } 3263 | }, 3264 | "node_modules/supports-color": { 3265 | "version": "7.2.0", 3266 | "dev": true, 3267 | "license": "MIT", 3268 | "dependencies": { 3269 | "has-flag": "^4.0.0" 3270 | }, 3271 | "engines": { 3272 | "node": ">=8" 3273 | } 3274 | }, 3275 | "node_modules/tar-fs": { 3276 | "version": "2.1.2", 3277 | "dev": true, 3278 | "license": "MIT", 3279 | "optional": true, 3280 | "dependencies": { 3281 | "chownr": "^1.1.1", 3282 | "mkdirp-classic": "^0.5.2", 3283 | "pump": "^3.0.0", 3284 | "tar-stream": "^2.1.4" 3285 | } 3286 | }, 3287 | "node_modules/tar-stream": { 3288 | "version": "2.2.0", 3289 | "dev": true, 3290 | "license": "MIT", 3291 | "optional": true, 3292 | "dependencies": { 3293 | "bl": "^4.0.3", 3294 | "end-of-stream": "^1.4.1", 3295 | "fs-constants": "^1.0.0", 3296 | "inherits": "^2.0.3", 3297 | "readable-stream": "^3.1.1" 3298 | }, 3299 | "engines": { 3300 | "node": ">=6" 3301 | } 3302 | }, 3303 | "node_modules/tmp": { 3304 | "version": "0.2.3", 3305 | "dev": true, 3306 | "license": "MIT", 3307 | "engines": { 3308 | "node": ">=14.14" 3309 | } 3310 | }, 3311 | "node_modules/to-regex-range": { 3312 | "version": "5.0.1", 3313 | "dev": true, 3314 | "license": "MIT", 3315 | "dependencies": { 3316 | "is-number": "^7.0.0" 3317 | }, 3318 | "engines": { 3319 | "node": ">=8.0" 3320 | } 3321 | }, 3322 | "node_modules/ts-api-utils": { 3323 | "version": "2.0.1", 3324 | "dev": true, 3325 | "license": "MIT", 3326 | "engines": { 3327 | "node": ">=18.12" 3328 | }, 3329 | "peerDependencies": { 3330 | "typescript": ">=4.8.4" 3331 | } 3332 | }, 3333 | "node_modules/tslib": { 3334 | "version": "2.8.1", 3335 | "dev": true, 3336 | "license": "0BSD" 3337 | }, 3338 | "node_modules/tunnel": { 3339 | "version": "0.0.6", 3340 | "dev": true, 3341 | "license": "MIT", 3342 | "engines": { 3343 | "node": ">=0.6.11 <=0.7.0 || >=0.7.3" 3344 | } 3345 | }, 3346 | "node_modules/tunnel-agent": { 3347 | "version": "0.6.0", 3348 | "dev": true, 3349 | "license": "Apache-2.0", 3350 | "optional": true, 3351 | "dependencies": { 3352 | "safe-buffer": "^5.0.1" 3353 | }, 3354 | "engines": { 3355 | "node": "*" 3356 | } 3357 | }, 3358 | "node_modules/type-check": { 3359 | "version": "0.4.0", 3360 | "dev": true, 3361 | "license": "MIT", 3362 | "dependencies": { 3363 | "prelude-ls": "^1.2.1" 3364 | }, 3365 | "engines": { 3366 | "node": ">= 0.8.0" 3367 | } 3368 | }, 3369 | "node_modules/typed-rest-client": { 3370 | "version": "1.8.11", 3371 | "dev": true, 3372 | "license": "MIT", 3373 | "dependencies": { 3374 | "qs": "^6.9.1", 3375 | "tunnel": "0.0.6", 3376 | "underscore": "^1.12.1" 3377 | } 3378 | }, 3379 | "node_modules/typescript": { 3380 | "version": "5.7.3", 3381 | "dev": true, 3382 | "license": "Apache-2.0", 3383 | "bin": { 3384 | "tsc": "bin/tsc", 3385 | "tsserver": "bin/tsserver" 3386 | }, 3387 | "engines": { 3388 | "node": ">=14.17" 3389 | } 3390 | }, 3391 | "node_modules/uc.micro": { 3392 | "version": "2.1.0", 3393 | "dev": true, 3394 | "license": "MIT" 3395 | }, 3396 | "node_modules/underscore": { 3397 | "version": "1.13.7", 3398 | "dev": true, 3399 | "license": "MIT" 3400 | }, 3401 | "node_modules/undici": { 3402 | "version": "6.21.2", 3403 | "dev": true, 3404 | "license": "MIT", 3405 | "engines": { 3406 | "node": ">=18.17" 3407 | } 3408 | }, 3409 | "node_modules/undici-types": { 3410 | "version": "6.19.8", 3411 | "dev": true, 3412 | "license": "MIT" 3413 | }, 3414 | "node_modules/uri-js": { 3415 | "version": "4.4.1", 3416 | "dev": true, 3417 | "license": "BSD-2-Clause", 3418 | "dependencies": { 3419 | "punycode": "^2.1.0" 3420 | } 3421 | }, 3422 | "node_modules/util-deprecate": { 3423 | "version": "1.0.2", 3424 | "dev": true, 3425 | "license": "MIT", 3426 | "optional": true 3427 | }, 3428 | "node_modules/uuid": { 3429 | "version": "8.3.2", 3430 | "dev": true, 3431 | "license": "MIT", 3432 | "bin": { 3433 | "uuid": "dist/bin/uuid" 3434 | } 3435 | }, 3436 | "node_modules/vscode-jsonrpc": { 3437 | "version": "8.2.0", 3438 | "license": "MIT", 3439 | "engines": { 3440 | "node": ">=14.0.0" 3441 | } 3442 | }, 3443 | "node_modules/vscode-languageclient": { 3444 | "version": "9.0.1", 3445 | "license": "MIT", 3446 | "dependencies": { 3447 | "minimatch": "^5.1.0", 3448 | "semver": "^7.3.7", 3449 | "vscode-languageserver-protocol": "3.17.5" 3450 | }, 3451 | "engines": { 3452 | "vscode": "^1.82.0" 3453 | } 3454 | }, 3455 | "node_modules/vscode-languageclient/node_modules/minimatch": { 3456 | "version": "5.1.6", 3457 | "license": "ISC", 3458 | "dependencies": { 3459 | "brace-expansion": "^2.0.1" 3460 | }, 3461 | "engines": { 3462 | "node": ">=10" 3463 | } 3464 | }, 3465 | "node_modules/vscode-languageserver-protocol": { 3466 | "version": "3.17.5", 3467 | "license": "MIT", 3468 | "dependencies": { 3469 | "vscode-jsonrpc": "8.2.0", 3470 | "vscode-languageserver-types": "3.17.5" 3471 | } 3472 | }, 3473 | "node_modules/vscode-languageserver-types": { 3474 | "version": "3.17.5", 3475 | "license": "MIT" 3476 | }, 3477 | "node_modules/vscode-uri": { 3478 | "version": "3.1.0", 3479 | "license": "MIT" 3480 | }, 3481 | "node_modules/whatwg-encoding": { 3482 | "version": "3.1.1", 3483 | "dev": true, 3484 | "license": "MIT", 3485 | "dependencies": { 3486 | "iconv-lite": "0.6.3" 3487 | }, 3488 | "engines": { 3489 | "node": ">=18" 3490 | } 3491 | }, 3492 | "node_modules/whatwg-mimetype": { 3493 | "version": "4.0.0", 3494 | "dev": true, 3495 | "license": "MIT", 3496 | "engines": { 3497 | "node": ">=18" 3498 | } 3499 | }, 3500 | "node_modules/which": { 3501 | "version": "2.0.2", 3502 | "dev": true, 3503 | "license": "ISC", 3504 | "dependencies": { 3505 | "isexe": "^2.0.0" 3506 | }, 3507 | "bin": { 3508 | "node-which": "bin/node-which" 3509 | }, 3510 | "engines": { 3511 | "node": ">= 8" 3512 | } 3513 | }, 3514 | "node_modules/word-wrap": { 3515 | "version": "1.2.5", 3516 | "dev": true, 3517 | "license": "MIT", 3518 | "engines": { 3519 | "node": ">=0.10.0" 3520 | } 3521 | }, 3522 | "node_modules/wrap-ansi-cjs": { 3523 | "name": "wrap-ansi", 3524 | "version": "7.0.0", 3525 | "dev": true, 3526 | "license": "MIT", 3527 | "dependencies": { 3528 | "ansi-styles": "^4.0.0", 3529 | "string-width": "^4.1.0", 3530 | "strip-ansi": "^6.0.0" 3531 | }, 3532 | "engines": { 3533 | "node": ">=10" 3534 | }, 3535 | "funding": { 3536 | "url": "https://github.com/chalk/wrap-ansi?sponsor=1" 3537 | } 3538 | }, 3539 | "node_modules/wrappy": { 3540 | "version": "1.0.2", 3541 | "dev": true, 3542 | "license": "ISC", 3543 | "optional": true 3544 | }, 3545 | "node_modules/xml2js": { 3546 | "version": "0.5.0", 3547 | "dev": true, 3548 | "license": "MIT", 3549 | "dependencies": { 3550 | "sax": ">=0.6.0", 3551 | "xmlbuilder": "~11.0.0" 3552 | }, 3553 | "engines": { 3554 | "node": ">=4.0.0" 3555 | } 3556 | }, 3557 | "node_modules/xmlbuilder": { 3558 | "version": "11.0.1", 3559 | "dev": true, 3560 | "license": "MIT", 3561 | "engines": { 3562 | "node": ">=4.0" 3563 | } 3564 | }, 3565 | "node_modules/yallist": { 3566 | "version": "4.0.0", 3567 | "dev": true, 3568 | "license": "ISC" 3569 | }, 3570 | "node_modules/yauzl": { 3571 | "version": "2.10.0", 3572 | "dev": true, 3573 | "license": "MIT", 3574 | "dependencies": { 3575 | "buffer-crc32": "~0.2.3", 3576 | "fd-slicer": "~1.1.0" 3577 | } 3578 | }, 3579 | "node_modules/yazl": { 3580 | "version": "2.5.1", 3581 | "dev": true, 3582 | "license": "MIT", 3583 | "dependencies": { 3584 | "buffer-crc32": "~0.2.3" 3585 | } 3586 | }, 3587 | "node_modules/yocto-queue": { 3588 | "version": "0.1.0", 3589 | "dev": true, 3590 | "license": "MIT", 3591 | "engines": { 3592 | "node": ">=10" 3593 | }, 3594 | "funding": { 3595 | "url": "https://github.com/sponsors/sindresorhus" 3596 | } 3597 | } 3598 | } 3599 | } 3600 | -------------------------------------------------------------------------------- /package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "postgrestools", 3 | "publisher": "Supabase", 4 | "description": "Postgres Language Server right in your IDE.", 5 | "version": "1.2.1", 6 | "repository": { 7 | "type": "git", 8 | "url": "https://github.com/supabase-community/postgrestools-vscode" 9 | }, 10 | "bugs": { 11 | "url": "https://github.com/supabase-community/postgres-language-server" 12 | }, 13 | "engines": { 14 | "vscode": "^1.96.2" 15 | }, 16 | "icon": "icon.png", 17 | "categories": [ 18 | "Language Packs", 19 | "Linters", 20 | "Formatters" 21 | ], 22 | "keywords": [ 23 | "postgrestools", 24 | "lsp", 25 | "postgres", 26 | "supabase" 27 | ], 28 | "main": "./out/index.js", 29 | "activationEvents": [ 30 | "onStartupFinished" 31 | ], 32 | "contributes": { 33 | "commands": [ 34 | { 35 | "title": "PostgresTools: Start", 36 | "command": "postgrestools.start" 37 | }, 38 | { 39 | "title": "PostgresTools: Stop", 40 | "command": "postgrestools.stop" 41 | }, 42 | { 43 | "title": "PostgresTools: Restart", 44 | "command": "postgrestools.restart" 45 | }, 46 | { 47 | "title": "PostgresTools: Download Server", 48 | "command": "postgrestools.download" 49 | }, 50 | { 51 | "title": "PostgresTools: Hard Reset (Delete All Temp & Global Binaries)", 52 | "command": "postgrestools.reset" 53 | }, 54 | { 55 | "title": "PostgresTools: Get Current Version", 56 | "command": "postgrestools.currentVersion" 57 | }, 58 | { 59 | "title": "PostgresTools: Copy Latest Server Logfile", 60 | "command": "postgrestools.copyLatestLogfile" 61 | } 62 | ], 63 | "configuration": { 64 | "title": "PostgresTools", 65 | "properties": { 66 | "postgrestools.enabled": { 67 | "type": "boolean", 68 | "description": "Whether to enable the PostgresTools extension.", 69 | "default": true, 70 | "scope": "resource" 71 | }, 72 | "postgrestools.allowDownloadPrereleases": { 73 | "type": "boolean", 74 | "description": "Allows selecting prereleases when downloading the binary via this extension", 75 | "default": false, 76 | "scope": "resource" 77 | }, 78 | "postgrestools.allowVersionChecks": { 79 | "type": "boolean", 80 | "description": "If set, the extension will check periodically whether a new version for PostgresTools is available and if so notify the user.", 81 | "default": true, 82 | "scope": "resource" 83 | }, 84 | "postgrestools.configFile": { 85 | "type": "string", 86 | "description": "Path to the `postgrestools.jsonc` file. You don't need to set this if the file is on root level of your project.", 87 | "scope": "resource" 88 | }, 89 | "postgrestools.bin": { 90 | "oneOf": [ 91 | { 92 | "type": "string", 93 | "description": "Path to the PostgresTools Language Server binary", 94 | "examples": [ 95 | "/path/to/postgrestools", 96 | "./path/to/postgrestools" 97 | ] 98 | }, 99 | { 100 | "type": "object", 101 | "description": "Platform-specific paths to the PostgresTools Language Server binary", 102 | "examples": [ 103 | { 104 | "linux-x64": "/path/to/postgrestools", 105 | "darwin-arm64": "./path/to/postgrestools", 106 | "win32-x64": "/path/to/postgrestools.exe" 107 | } 108 | ] 109 | } 110 | ], 111 | "scope": "resource" 112 | } 113 | } 114 | } 115 | }, 116 | "scripts": { 117 | "vscode:prepublish": "npm run compile", 118 | "compile": "tsc -p ./", 119 | "watch": "tsc -watch -p ./", 120 | "pretest": "npm run compile && npm run lint", 121 | "lint": "eslint src", 122 | "publish": "vsce package && vsce publish" 123 | }, 124 | "devDependencies": { 125 | "@types/node": "20.17.23", 126 | "@types/vscode": "^1.96.2", 127 | "@typescript-eslint/eslint-plugin": "^8.22.0", 128 | "@typescript-eslint/parser": "^8.22.0", 129 | "@vscode/vsce": "3.3.2", 130 | "eslint": "^9.19.0", 131 | "typescript": "^5.7.3" 132 | }, 133 | "dependencies": { 134 | "vscode-languageclient": "9.0.1", 135 | "vscode-uri": "3.1.0" 136 | } 137 | } 138 | -------------------------------------------------------------------------------- /seed.sql: -------------------------------------------------------------------------------- 1 | drop table if exists users; 2 | 3 | create table 4 | users ( 5 | name text, 6 | id serial primary key, 7 | email varchar(255) 8 | ); 9 | 10 | insert into 11 | users (name, email) 12 | values 13 | ('Julian', 'j@gmail.com'); -------------------------------------------------------------------------------- /src/binary-finder-strategies.ts: -------------------------------------------------------------------------------- 1 | import { Uri, window } from "vscode"; 2 | import { logger } from "./logger"; 3 | import { delimiter, dirname, join } from "node:path"; 4 | import { CONSTANTS } from "./constants"; 5 | import { fileExists } from "./utils"; 6 | import { createRequire } from "node:module"; 7 | import { getConfig } from "./config"; 8 | import { downloadPglt, getDownloadedBinary } from "./downloader"; 9 | 10 | export interface BinaryFindStrategy { 11 | name: string; 12 | find(path: Uri): Promise; 13 | } 14 | 15 | /** 16 | * The user can specify a PostgresTools binary in the VSCode settings. 17 | * 18 | * This can be done in two ways: 19 | * 20 | * 1. A static string that points to a binary. The extension will try to retrieve the binary from there. 21 | * 22 | * 2. An object with OS & arch combinations as keys and binary paths as values. 23 | * The extension will try to retrieve the binary from the key matching the current OS and arch. 24 | * 25 | * Config Example: 26 | * ```json 27 | * { 28 | * "postgrestools.bin": { 29 | * "linux-x64": "/path/to/postgrestools", 30 | * "darwin-arm64": "/path/to/postgrestools", 31 | * "win32-x64": "/path/to/postgrestools.exe" 32 | * } 33 | * } 34 | */ 35 | export const vsCodeSettingsStrategy: BinaryFindStrategy = { 36 | name: "VSCode Settings Strategy", 37 | async find(path: Uri) { 38 | logger.debug("Trying to find PostgresTools binary via VSCode Settings"); 39 | 40 | type BinSetting = string | Record | undefined; 41 | let binSetting: BinSetting = getConfig("bin", { 42 | scope: path, 43 | }); 44 | 45 | if (!binSetting) { 46 | logger.debug("Binary path not set in VSCode Settings"); 47 | return null; 48 | } 49 | 50 | if (typeof binSetting === "object") { 51 | logger.debug( 52 | "Binary Setting is an object, extracting relevant platform", 53 | { binSetting } 54 | ); 55 | 56 | const relevantSetting = binSetting[CONSTANTS.platformIdentifier]; 57 | if (relevantSetting) { 58 | logger.debug( 59 | "Found matching setting for platform in VSCode Settings, assigning as string", 60 | { 61 | setting: relevantSetting, 62 | platformIdentifier: CONSTANTS.platformIdentifier, 63 | } 64 | ); 65 | binSetting = relevantSetting; 66 | } 67 | } 68 | 69 | if (typeof binSetting === "string") { 70 | logger.debug("Binary Setting is a string", { binSetting }); 71 | 72 | const resolvedPath = binSetting.startsWith(".") 73 | ? Uri.joinPath(path, binSetting).fsPath 74 | : binSetting; 75 | 76 | logger.debug("Looking for binary at path", { resolvedPath }); 77 | 78 | const postgrestools = Uri.file(resolvedPath); 79 | 80 | if (await fileExists(postgrestools)) { 81 | return postgrestools; 82 | } 83 | } 84 | 85 | logger.debug("No PostgresTools binary found in VSCode settings."); 86 | 87 | return null; 88 | }, 89 | }; 90 | 91 | /** 92 | * Task: 93 | * Search the binary in node modules. 94 | * Search for the sub-packages that the binary tries to use with npm. 95 | * Use node's `createRequire` – what's that? 96 | * Resolve the *main* package.json – the one used by @postgrestools/postgrestools. 97 | * In those node_modules, you should see the installed optional dependency. 98 | */ 99 | export const nodeModulesStrategy: BinaryFindStrategy = { 100 | name: "Node Modules Strategy", 101 | async find(path: Uri) { 102 | logger.debug("Trying to find PostgresTools binary in Node Modules"); 103 | 104 | if (!path) { 105 | logger.debug("No local path, skipping."); 106 | return null; 107 | } 108 | 109 | const postgrestoolsPackageNameJson = `${CONSTANTS.npmPackageName}/package.json`; 110 | 111 | logger.info(`Searching for node_modules package`, { 112 | postgrestoolsPackageNameJson, 113 | }); 114 | 115 | let requirePgltPackage: NodeJS.Require; 116 | try { 117 | /** 118 | * Create a scoped require function that can require modules from the 119 | * package installed via npm. 120 | * 121 | * We're essentially searching for the installed package in the current dir, and requiring from its node_modules. 122 | * `package.json` serves as a target to resolve the root of the package. 123 | */ 124 | requirePgltPackage = createRequire( 125 | require.resolve(postgrestoolsPackageNameJson, { 126 | paths: [path.fsPath], // note: global ~/.node_modules is always searched 127 | }) 128 | ); 129 | } catch (err: unknown) { 130 | if ( 131 | err instanceof Error && 132 | err.message.toLowerCase().includes("cannot find module") 133 | ) { 134 | logger.debug(`User does not use node_modules`); 135 | return null; 136 | } else { 137 | throw err; 138 | } 139 | } 140 | 141 | logger.debug("Created require function!"); 142 | 143 | const packageName = CONSTANTS.platformSpecificNodePackageName; 144 | if (packageName === undefined) { 145 | logger.debug( 146 | `No package for current platform available in node_modules`, 147 | { 148 | os: process.platform, 149 | arch: process.arch, 150 | } 151 | ); 152 | return null; 153 | } 154 | 155 | logger.debug(`Resolving bin package at nested ${packageName}/package.json`); 156 | 157 | const binPackage = dirname( 158 | requirePgltPackage.resolve(`${packageName}/package.json`) 159 | ); 160 | 161 | logger.debug(`Resolved binpackage`, { binPackage }); 162 | 163 | const postgrestoolsPath = join( 164 | binPackage, 165 | CONSTANTS.platformSpecificBinaryName 166 | ); 167 | const postgrestools = Uri.file(postgrestoolsPath); 168 | 169 | if (await fileExists(postgrestools)) { 170 | return postgrestools; 171 | } 172 | 173 | logger.debug(`Unable to find PostgresTools in path ${postgrestoolsPath}`); 174 | 175 | return null; 176 | }, 177 | }; 178 | 179 | export const yarnPnpStrategy: BinaryFindStrategy = { 180 | name: "Yarn PnP Strategy", 181 | async find(path: Uri) { 182 | logger.debug("Trying to find PostgresTools binary in Yarn Plug'n'Play"); 183 | 184 | if (!path) { 185 | logger.debug("No local path, skipping."); 186 | return null; 187 | } 188 | 189 | for (const ext of ["cjs", "js"]) { 190 | const pnpFile = Uri.joinPath(path, `.pnp.${ext}`); 191 | 192 | if (!(await fileExists(pnpFile))) { 193 | logger.debug(`Couldn't find Plug'n'Play file with ext '${ext}'`); 194 | continue; 195 | } 196 | 197 | /** 198 | * Load the pnp file, so we can use the exported 199 | * `resolveRequest` method. 200 | * 201 | * `resolveRequest(request, issuer)` takes a request for a dependency and an issuer 202 | * that depends on said dependency. 203 | */ 204 | const yarnPnpApi = require(pnpFile.fsPath); 205 | 206 | /** 207 | * Issue a request to the PostgresTools package.json from the current dir. 208 | */ 209 | const postgrestoolsPackage = yarnPnpApi.resolveRequest( 210 | `${CONSTANTS.npmPackageName}/package.json`, 211 | path.fsPath 212 | ); 213 | 214 | if (!postgrestoolsPackage) { 215 | logger.debug( 216 | "Unable to find PostgresTools package via Yarn Plug'n'Play API" 217 | ); 218 | continue; 219 | } 220 | 221 | const packageName = CONSTANTS.platformSpecificNodePackageName; 222 | if (packageName === undefined) { 223 | logger.debug(`No package for current platform available in yarn pnp`, { 224 | os: process.platform, 225 | arch: process.arch, 226 | }); 227 | return null; 228 | } 229 | 230 | /** 231 | * Return URI to the platform-specific binary that the found main package depends on. 232 | */ 233 | return Uri.file( 234 | yarnPnpApi.resolveRequest( 235 | `${packageName}/${CONSTANTS.platformSpecificBinaryName}`, 236 | postgrestoolsPackage 237 | ) 238 | ); 239 | } 240 | 241 | logger.debug("Couldn't find PostgresTools binary via Yarn Plug'n'Play"); 242 | 243 | return null; 244 | }, 245 | }; 246 | 247 | export const pathEnvironmentVariableStrategy: BinaryFindStrategy = { 248 | name: "PATH Env Var Strategy", 249 | async find() { 250 | const pathEnv = process.env.PATH; 251 | 252 | logger.debug("Trying to find PostgresTools binary in PATH env var"); 253 | 254 | if (!pathEnv) { 255 | logger.debug("Path env var not found"); 256 | return null; 257 | } 258 | 259 | for (const dir of pathEnv.split(delimiter)) { 260 | logger.debug(`Checking ${dir}`); 261 | 262 | const postgrestools = Uri.joinPath( 263 | Uri.file(dir), 264 | CONSTANTS.platformSpecificBinaryName 265 | ); 266 | 267 | if (await fileExists(postgrestools)) { 268 | return postgrestools; 269 | } 270 | } 271 | 272 | logger.debug("Couldn't determine binary in PATH env var"); 273 | 274 | return null; 275 | }, 276 | }; 277 | 278 | export const downloadPgltStrategy: BinaryFindStrategy = { 279 | name: "Download PostgresTools Strategy", 280 | async find() { 281 | logger.debug(`Trying to find downloaded PostgresTools binary`); 282 | 283 | const downloadedBinary = await getDownloadedBinary(); 284 | 285 | if (downloadedBinary) { 286 | logger.info( 287 | `Using previously downloaded version ${downloadedBinary.version} at ${downloadedBinary.binPath.fsPath}` 288 | ); 289 | 290 | return downloadedBinary.binPath; 291 | } 292 | 293 | const proceed = 294 | (await window.showInformationMessage( 295 | "You've opened a supported file outside of a PostgresTools project, and no installed PostgresTools binary could be found on your system. Would you like to download and install PostgresTools?", 296 | "Download and install", 297 | "No" 298 | )) === "Download and install"; 299 | 300 | if (!proceed) { 301 | logger.debug(`Decided not to download binary, aborting`); 302 | return null; 303 | } 304 | 305 | return await downloadPglt(); 306 | }, 307 | }; 308 | -------------------------------------------------------------------------------- /src/binary-finder.ts: -------------------------------------------------------------------------------- 1 | import { Uri } from "vscode"; 2 | import { 3 | BinaryFindStrategy, 4 | downloadPgltStrategy, 5 | nodeModulesStrategy, 6 | pathEnvironmentVariableStrategy, 7 | vsCodeSettingsStrategy, 8 | yarnPnpStrategy, 9 | } from "./binary-finder-strategies"; 10 | import { logger } from "./logger"; 11 | 12 | type Strategy = { 13 | label: string; 14 | strategy: BinaryFindStrategy; 15 | onSuccess: (u: Uri) => void; 16 | condition?: (path?: Uri) => Promise; 17 | }; 18 | 19 | const LOCAL_STRATEGIES: Strategy[] = [ 20 | { 21 | label: "VSCode Settings", 22 | strategy: vsCodeSettingsStrategy, 23 | onSuccess: (uri) => 24 | logger.debug(`Found Binary in VSCode Settings (postgrestools.bin)`, { 25 | path: uri.fsPath, 26 | }), 27 | }, 28 | { 29 | label: "NPM node_modules", 30 | strategy: nodeModulesStrategy, 31 | onSuccess: (uri) => 32 | logger.debug(`Found Binary in Node Modules`, { 33 | path: uri.fsPath, 34 | }), 35 | }, 36 | { 37 | label: "Yarn Plug'n'Play node_modules", 38 | strategy: yarnPnpStrategy, 39 | onSuccess: (uri) => 40 | logger.debug(`Found Binary in Yarn PnP`, { 41 | path: uri.fsPath, 42 | }), 43 | }, 44 | { 45 | label: "PATH Environment Variable", 46 | strategy: pathEnvironmentVariableStrategy, 47 | onSuccess: (uri) => 48 | logger.debug(`Found Binary in PATH Environment Variable`, { 49 | path: uri.fsPath, 50 | }), 51 | }, 52 | { 53 | label: "Downloaded Binary", 54 | strategy: downloadPgltStrategy, 55 | onSuccess: (uri) => 56 | logger.debug(`Found downloaded binary`, { 57 | path: uri.fsPath, 58 | }), 59 | }, 60 | ]; 61 | 62 | export class BinaryFinder { 63 | static async find(path: Uri) { 64 | const binary = await this.attemptFind(LOCAL_STRATEGIES, path); 65 | 66 | if (!binary) { 67 | logger.debug("Unable to find binary locally."); 68 | } 69 | 70 | return binary; 71 | } 72 | 73 | private static async attemptFind(strategies: Strategy[], path: Uri) { 74 | for (const { strategy, onSuccess, condition, label } of strategies) { 75 | if (condition && !(await condition(path))) { 76 | continue; 77 | } 78 | 79 | try { 80 | const binary = await strategy.find(path); 81 | if (binary) { 82 | onSuccess(binary); 83 | return { bin: binary, label }; 84 | } else { 85 | logger.info(`Binary not found with strategy`, { 86 | strategy: strategy.name, 87 | }); 88 | } 89 | } catch (err: unknown) { 90 | logger.error(`${strategy.name} returned an error`, { err }); 91 | continue; 92 | } 93 | } 94 | } 95 | } 96 | -------------------------------------------------------------------------------- /src/commands.ts: -------------------------------------------------------------------------------- 1 | import { Uri, window, workspace } from "vscode"; 2 | import { downloadPglt, getDownloadedBinary } from "./downloader"; 3 | import { restart, start, stop } from "./lifecycle"; 4 | import { logger } from "./logger"; 5 | import { state } from "./state"; 6 | import { 7 | clearGlobalBinaries, 8 | clearTemporaryBinaries, 9 | dirExists, 10 | getVersion, 11 | } from "./utils"; 12 | import { Releases } from "./releases"; 13 | import { join } from "path"; 14 | import os from "node:os"; 15 | 16 | /** 17 | * These commands are exposed to the user via the Command Palette. 18 | */ 19 | export class UserFacingCommands { 20 | static async start() { 21 | await start(); 22 | } 23 | 24 | static async stop() { 25 | await stop(); 26 | } 27 | 28 | static async restart() { 29 | await restart(); 30 | } 31 | 32 | /** 33 | * When calling this command, the user will be prompted to select a version of 34 | * the PostgresTools CLI to install. The selected version will be downloaded and stored 35 | * in VS Code's global storage directory. 36 | */ 37 | static async download() { 38 | await downloadPglt(); 39 | } 40 | 41 | /** 42 | * Stops and restarts the PostgresTools extension, resetting state and cleaning up temporary binaries. 43 | */ 44 | static async reset() { 45 | await stop(); 46 | await clearTemporaryBinaries(); 47 | await clearGlobalBinaries(); 48 | 49 | await Releases.refresh(); 50 | 51 | await state.context.globalState.update("lastNotifiedOfUpdate", undefined); 52 | 53 | state.activeSession = undefined; 54 | state.activeProject = undefined; 55 | logger.info("PostgresTools extension was reset"); 56 | 57 | await start(); 58 | } 59 | 60 | static async currentVersion() { 61 | const session = state.activeSession; 62 | 63 | if (!session) { 64 | window.showInformationMessage("No PostgresTools version installed."); 65 | return; 66 | } 67 | 68 | const version = await getVersion(session.bin); 69 | 70 | if (!version) { 71 | window.showInformationMessage("No PostgresTools version installed."); 72 | } else { 73 | window.showInformationMessage( 74 | `Currently installed PostgresTools version is ${version}.` 75 | ); 76 | window.showInformationMessage( 77 | `Using binary from "${session.binaryStrategyLabel}".` 78 | ); 79 | } 80 | } 81 | 82 | static async copyLatestLogfile() { 83 | logger.info("Starting to copy latest log file…"); 84 | 85 | let logdir; 86 | 87 | if (process.env.PGT_LOG_PATH) { 88 | logdir = Uri.file(process.env.PGT_LOG_PATH); 89 | } else { 90 | /* 91 | * Looks are placed at different locations based on the platform. 92 | * Linux: /home/alice/.cache/pgt 93 | * Win: C:\Users\Alice\AppData\Local\supabase-community\pgt\cache 94 | * Mac: /Users/Alice/Library/Caches/dev.supabase-community.pgt 95 | */ 96 | switch (process.platform) { 97 | case "darwin": { 98 | logdir = Uri.file( 99 | join( 100 | os.homedir(), 101 | "Library", 102 | "Caches", 103 | "dev.supabase-community.pgt" 104 | ) 105 | ); 106 | break; 107 | } 108 | case "linux": { 109 | logdir = Uri.file(join(os.homedir(), ".cache", "pgt")); 110 | break; 111 | } 112 | case "win32": { 113 | logdir = Uri.file( 114 | join( 115 | os.homedir(), 116 | "AppData", 117 | "Local", 118 | "supabase-community", 119 | "pgt", 120 | "cache" 121 | ) 122 | ); 123 | break; 124 | } 125 | default: { 126 | window.showErrorMessage( 127 | `Unsupported Platform: ${process.platform}. PostgresTools only runs on linux, darwin and windows.` 128 | ); 129 | return; 130 | } 131 | } 132 | } 133 | 134 | logger.info("Determined log directory location", { 135 | logdir: logdir.fsPath, 136 | }); 137 | 138 | if (!(await dirExists(logdir))) { 139 | window.showErrorMessage(`Did not find expected log directory.`); 140 | return; 141 | } 142 | 143 | const logFiles = await workspace.fs 144 | .readDirectory(logdir) 145 | .then((files) => files.filter(([name]) => name.startsWith("server.log"))); 146 | 147 | if (logFiles.length === 0) { 148 | window.showErrorMessage(`No log files found in log directory.`); 149 | return; 150 | } 151 | 152 | logger.info(`Found ${logFiles.length} log files.`); 153 | 154 | if (!state.activeProject) { 155 | window.showErrorMessage( 156 | `No active project, can't determine a location to copy the log file.` 157 | ); 158 | return; 159 | } 160 | 161 | logFiles.sort((a, b) => { 162 | const [dateA, idxA] = getDatePartsFromLogFile(a[0]); 163 | const [dateB, idxB] = getDatePartsFromLogFile(b[0]); 164 | 165 | if (dateA.getTime() === dateB.getTime()) { 166 | return idxB - idxA; 167 | } else { 168 | return dateB.getTime() - dateA.getTime(); 169 | } 170 | }); 171 | 172 | const [latestFilename] = logFiles[0]; 173 | 174 | logger.info(`Identified latest log file.`, { 175 | filename: latestFilename, 176 | }); 177 | 178 | try { 179 | const target = Uri.joinPath(state.activeProject.path, "server.log"); 180 | 181 | await workspace.fs.copy(Uri.joinPath(logdir, latestFilename), target); 182 | 183 | window.showInformationMessage(`Copied log file to ${target.fsPath}.`); 184 | } catch (err) { 185 | logger.error(`Error copying log file: ${err}`); 186 | window.showErrorMessage( 187 | `Error copying log file. View Output for more information.` 188 | ); 189 | } 190 | } 191 | } 192 | 193 | /** Expected format is server.log.2025-01-01-17 */ 194 | function getDatePartsFromLogFile(filename: string): [Date, number] { 195 | try { 196 | const last = filename.split(".").pop()!; 197 | const [year, month, day, idx] = last.split("-"); 198 | 199 | if (!year || !month || !day || !idx || Number.isNaN(+idx)) { 200 | throw new Error(); 201 | } 202 | 203 | return [new Date(`${year}-${month}-${day}`), +idx]; 204 | } catch { 205 | logger.warn(`Unexpected log file name: ${filename}`); 206 | return [new Date(0), 999]; 207 | } 208 | } 209 | -------------------------------------------------------------------------------- /src/config.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type ConfigurationScope, 3 | WorkspaceConfiguration, 4 | type WorkspaceFolder, 5 | workspace, 6 | } from "vscode"; 7 | 8 | /** 9 | * This function retrieves a setting from the workspace configuration. 10 | * Settings are looked up under the "postgrestools" prefix. 11 | * 12 | * @param key The key of the setting to retrieve 13 | */ 14 | export const getFullConfig = ( 15 | options: { 16 | scope?: ConfigurationScope; 17 | } = {} 18 | ): WorkspaceConfiguration | undefined => { 19 | return workspace.getConfiguration("postgrestools", options.scope); 20 | }; 21 | 22 | /** 23 | * This function retrieves a setting from the workspace configuration. 24 | * Settings are looked up under the "postgrestools" prefix. 25 | * 26 | * @param key The key of the setting to retrieve 27 | */ 28 | export const getConfig = ( 29 | key: string, 30 | options: { 31 | scope?: ConfigurationScope; 32 | } = {} 33 | ): T | undefined => { 34 | return workspace.getConfiguration("postgrestools", options.scope).get(key); 35 | }; 36 | 37 | /** 38 | * TODO: Can the "state.activeProject" also refer to a workspace, or just to a workspace-folder? 39 | */ 40 | export const isEnabledForFolder = (folder: WorkspaceFolder): boolean => { 41 | return !!getConfig("enabled", { scope: folder.uri }); 42 | }; 43 | -------------------------------------------------------------------------------- /src/constants.ts: -------------------------------------------------------------------------------- 1 | import { workspace } from "vscode"; 2 | import packageJson from "../package.json"; 3 | 4 | export enum OperatingMode { 5 | SingleFile = "single_file", // unsupported 6 | SingleRoot = "single_root", 7 | MultiRoot = "multi_root", 8 | } 9 | 10 | const npmPackageName = "@postgrestools/postgrestools"; 11 | 12 | /** 13 | * platform and arch are values injected into the node runtime. 14 | * We use the values documented on https://nodejs.org. 15 | */ 16 | const PACKAGE_NAMES: Record> = { 17 | win32: { 18 | x64: `@postgrestools/cli-x86_64-windows-msvc`, 19 | arm64: `@postgrestools/cli-aarch64-windows-msvc`, 20 | }, 21 | darwin: { 22 | x64: `@postgrestools/cli-x86_64-apple-darwin`, 23 | arm64: `@postgrestools/cli-aarch64-apple-darwin`, 24 | }, 25 | linux: { 26 | x64: `@postgrestools/cli-x86_64-linux-gnu`, 27 | arm64: `@postgrestools/cli-aarch64-linux-gnu`, 28 | }, 29 | }; 30 | 31 | const platformMappings: Record = { 32 | darwin: "apple-darwin", 33 | linux: "unknown-linux-gnu", 34 | win32: "pc-windows-msvc", 35 | }; 36 | 37 | const archMappings: Record = { 38 | arm64: "aarch64", 39 | x64: "x86_64", 40 | }; 41 | 42 | const _CONSTANTS = { 43 | displayName: packageJson.name, 44 | 45 | activationTimestamp: Date.now(), 46 | 47 | platformSpecificBinaryName: (() => { 48 | return `postgrestools${process.platform === "win32" ? ".exe" : ""}`; 49 | })(), 50 | 51 | /** 52 | * The name under which PostgresTools is published on npm. 53 | */ 54 | npmPackageName, 55 | 56 | platformSpecificNodePackageName: (() => { 57 | const platform: string = process.platform; 58 | const arch: string = process.arch; 59 | 60 | const pkg = PACKAGE_NAMES[platform]?.[arch]; 61 | 62 | // TS won't pick up on the possibility of this being undefined 63 | return pkg as string | undefined; 64 | })(), 65 | 66 | platformSpecificReleasedAssetName: (() => { 67 | let assetName = "postgrestools"; 68 | 69 | for (const [nodeArch, rustArch] of Object.entries(archMappings)) { 70 | if (nodeArch === process.arch) { 71 | assetName += `_${rustArch}`; 72 | } 73 | } 74 | 75 | for (const [nodePlatform, rustPlatform] of Object.entries( 76 | platformMappings 77 | )) { 78 | if (nodePlatform === process.platform) { 79 | assetName += `-${rustPlatform}`; 80 | } 81 | } 82 | 83 | return assetName; 84 | })(), 85 | 86 | currentMachineSupported: (() => { 87 | // In future release, we should also check whether the toolchain matches (Linux musl, GNU etc.) 88 | return !!(platformMappings[process.platform] && archMappings[process.arch]); 89 | })(), 90 | 91 | operatingMode: ((): OperatingMode => { 92 | if (workspace.workspaceFolders === undefined) { 93 | return OperatingMode.SingleFile; 94 | } 95 | 96 | if (workspace.workspaceFolders.length > 1) { 97 | return OperatingMode.MultiRoot; 98 | } 99 | 100 | return OperatingMode.SingleRoot; 101 | })(), 102 | 103 | platformIdentifier: (() => { 104 | return `${process.platform}-${process.arch}`; 105 | })(), 106 | 107 | globalStorageFolderForBinary: "global-bin", 108 | globalStorageFolderTmp: "tmp-bin", 109 | }; 110 | 111 | export const CONSTANTS: typeof _CONSTANTS = new Proxy(_CONSTANTS, { 112 | get(target, prop, receiver) { 113 | return Reflect.get(target, prop, receiver); 114 | }, 115 | set: () => true, 116 | deleteProperty: () => true, 117 | }); 118 | -------------------------------------------------------------------------------- /src/downloader.ts: -------------------------------------------------------------------------------- 1 | import { 2 | ProgressLocation, 3 | QuickPickItem, 4 | Uri, 5 | window, 6 | workspace, 7 | } from "vscode"; 8 | import { logger } from "./logger"; 9 | import { state } from "./state"; 10 | import { CONSTANTS } from "./constants"; 11 | import { fileExists, getVersion } from "./utils"; 12 | import { chmodSync } from "fs"; 13 | 14 | export async function downloadPglt(): Promise { 15 | logger.debug(`Downloading PostgresTools`); 16 | 17 | const versionToDownload = await promptVersionToDownload(); 18 | 19 | if (!versionToDownload) { 20 | logger.debug(`No version to download selected, aborting`); 21 | return null; 22 | } 23 | 24 | await window.withProgress( 25 | { 26 | title: `Downloading PostgresTools ${versionToDownload.label}`, 27 | location: ProgressLocation.Notification, 28 | }, 29 | () => downloadPgltVersion(versionToDownload.label) 30 | ); 31 | 32 | const downloaded = await getDownloadedBinary(); 33 | 34 | return downloaded?.binPath ?? null; 35 | } 36 | 37 | async function downloadPgltVersion(version: string): Promise { 38 | const url = `https://github.com/supabase-community/postgres_lsp/releases/download/${version}/${CONSTANTS.platformSpecificReleasedAssetName}`; 39 | 40 | logger.debug(`Attempting to download binary asset from Github`, { url }); 41 | 42 | let binary: ArrayBuffer; 43 | 44 | try { 45 | binary = await fetch(url, { 46 | headers: { 47 | Accept: "application/octet-stream", 48 | }, 49 | }) 50 | .then((r) => r.blob()) 51 | .then((b) => b.arrayBuffer()); 52 | } catch (error: unknown) { 53 | logger.error(`Failed to download binary`, { error }); 54 | window.showErrorMessage( 55 | `Failed to download binary version ${version} from ${url}.\n\n${error}` 56 | ); 57 | return; 58 | } 59 | 60 | const binPath = getInstalledBinaryPath(); 61 | 62 | try { 63 | await workspace.fs.writeFile(binPath, new Uint8Array(binary)); 64 | chmodSync(binPath.fsPath, 0o755); 65 | const successMsg = `Downloaded PostgresTools ${version} to ${binPath.fsPath}`; 66 | logger.info(successMsg); 67 | window.showInformationMessage(successMsg); 68 | } catch (error) { 69 | logger.error(`Failed to save downloaded binary`, { error }); 70 | window.showErrorMessage(`Failed to save binary.\n\n${error}`); 71 | return; 72 | } 73 | } 74 | 75 | export async function getDownloadedBinary(): Promise<{ 76 | version: string; 77 | binPath: Uri; 78 | } | null> { 79 | logger.debug(`Getting downloaded version`); 80 | 81 | const bin = getInstalledBinaryPath(); 82 | 83 | if (await fileExists(bin)) { 84 | const version = await getVersion(bin); 85 | if (!version) { 86 | throw new Error("Just verified file exists, but it doesn't anymore."); 87 | } 88 | 89 | logger.debug(`Found downloaded version and binary`, { 90 | path: bin.fsPath, 91 | version, 92 | }); 93 | 94 | return { 95 | binPath: bin, 96 | version, 97 | }; 98 | } 99 | 100 | logger.info(`Downloaded binary does not exist.`, { 101 | binPath: bin, 102 | }); 103 | 104 | return null; 105 | } 106 | 107 | async function promptVersionToDownload() { 108 | logger.debug(`Prompting user to select PostgresTools version to download`); 109 | 110 | const itemsPromise: Promise = new Promise( 111 | async (resolve) => { 112 | const downloadedBinary = await getDownloadedBinary() 113 | .then((it) => it?.version) 114 | .catch(() => undefined); 115 | 116 | logger.debug(`Retrieved downloaded version`, { 117 | downloadedVersion: downloadedBinary, 118 | }); 119 | 120 | const availableVersions = state.releases.all(); 121 | 122 | const items: QuickPickItem[] = availableVersions.map((release, index) => { 123 | const descriptions = []; 124 | 125 | if (index === 0) { 126 | descriptions.push("latest"); 127 | } 128 | 129 | if (release.prerelease) { 130 | descriptions.push("prerelease"); 131 | } 132 | 133 | if (downloadedBinary === release.tag_name) { 134 | descriptions.push("(currently installed)"); 135 | } 136 | 137 | return { 138 | label: release.tag_name, 139 | description: descriptions.join(", "), 140 | alwaysShow: index < 3, 141 | }; 142 | }); 143 | 144 | resolve(items); 145 | } 146 | ); 147 | 148 | return window.showQuickPick(itemsPromise, { 149 | title: "Select PostgresTools version to download", 150 | placeHolder: "Select PostgresTools version to download", 151 | }); 152 | } 153 | 154 | function getInstalledBinaryPath() { 155 | return Uri.joinPath( 156 | state.context.globalStorageUri, 157 | CONSTANTS.globalStorageFolderForBinary, 158 | CONSTANTS.platformSpecificBinaryName 159 | ); 160 | } 161 | -------------------------------------------------------------------------------- /src/extension.ts: -------------------------------------------------------------------------------- 1 | import { 2 | type ConfigurationChangeEvent, 3 | commands, 4 | window, 5 | workspace, 6 | } from "vscode"; 7 | import { UserFacingCommands } from "./commands"; 8 | import { restart, start, stop } from "./lifecycle"; 9 | import { logger } from "./logger"; 10 | import { state } from "./state"; 11 | import { debounce } from "./utils"; 12 | import { updateHidden } from "./status-bar"; 13 | 14 | /** 15 | * This function is responsible for booting the PostgresTools extension. It is called 16 | * when the extension is activated. 17 | */ 18 | export const createExtension = async () => { 19 | registerUserFacingCommands(); 20 | await start(); 21 | listenForConfigurationChanges(); 22 | listenForActiveTextEditorChange(); 23 | }; 24 | 25 | /** 26 | * This function is responsible for shutting down the PostgresTools extension. It is 27 | * called when the extension is deactivated and will trigger a cleanup of the 28 | * extension's state and resources. 29 | */ 30 | export const destroyExtension = async () => { 31 | await stop(); 32 | }; 33 | 34 | const registerUserFacingCommands = () => { 35 | state.context.subscriptions.push( 36 | commands.registerCommand("postgrestools.start", UserFacingCommands.start), 37 | commands.registerCommand("postgrestools.stop", UserFacingCommands.stop), 38 | commands.registerCommand( 39 | "postgrestools.restart", 40 | UserFacingCommands.restart 41 | ), 42 | commands.registerCommand( 43 | "postgrestools.download", 44 | UserFacingCommands.download 45 | ), 46 | commands.registerCommand("postgrestools.reset", UserFacingCommands.reset), 47 | commands.registerCommand( 48 | "postgrestools.currentVersion", 49 | UserFacingCommands.currentVersion 50 | ), 51 | commands.registerCommand( 52 | "postgrestools.copyLatestLogfile", 53 | UserFacingCommands.copyLatestLogfile 54 | ) 55 | ); 56 | 57 | logger.info("User-facing commands registered"); 58 | }; 59 | 60 | /** 61 | * This function sets up a listener for configuration changes in the `postgrestools` 62 | * namespace. When a configuration change is detected, the extension is 63 | * restarted to reflect the new configuration. 64 | */ 65 | const listenForConfigurationChanges = () => { 66 | const debouncedConfigurationChangeHandler = debounce( 67 | (event: ConfigurationChangeEvent) => { 68 | if (event.affectsConfiguration("postgrestools")) { 69 | logger.info("Configuration change detected."); 70 | if (!["restarting", "stopping"].includes(state.state)) { 71 | restart(); 72 | } 73 | } 74 | } 75 | ); 76 | 77 | state.context.subscriptions.push( 78 | workspace.onDidChangeConfiguration(debouncedConfigurationChangeHandler) 79 | ); 80 | 81 | logger.info("Started listening for configuration changes"); 82 | }; 83 | 84 | /** 85 | * This function listens for changes to the active text editor and updates the 86 | * active project accordingly. This change is then reflected throughout the 87 | * extension automatically. Notably, this triggers the status bar to update 88 | * with the active project. 89 | */ 90 | const listenForActiveTextEditorChange = () => { 91 | state.context.subscriptions.push( 92 | window.onDidChangeActiveTextEditor((editor) => { 93 | updateHidden(editor); 94 | }) 95 | ); 96 | 97 | logger.info("Started listening for active text editor changes"); 98 | 99 | updateHidden(window.activeTextEditor); 100 | }; 101 | -------------------------------------------------------------------------------- /src/index.ts: -------------------------------------------------------------------------------- 1 | // The module 'vscode' contains the VS Code extensibility API 2 | // Import the module and reference it with the alias vscode in your code below 3 | import * as vscode from "vscode"; 4 | import { logger } from "./logger"; 5 | import { state } from "./state"; 6 | import { createExtension, destroyExtension } from "./extension"; 7 | import { getConfig, getFullConfig } from "./config"; 8 | 9 | // This method is called when your extension is activated 10 | // Your extension is activated the very first time the command is executed 11 | export async function activate(context: vscode.ExtensionContext) { 12 | logger.clear(); 13 | logger.info( 14 | `PostgresTools extension ${context.extension.packageJSON.version} activated` 15 | ); 16 | state.context = context; 17 | 18 | const config = getFullConfig(); 19 | 20 | logger.info(`Starting with config…`, { config }); 21 | 22 | await createExtension(); 23 | } 24 | 25 | // This method is called when your extension is deactivated 26 | export async function deactivate() { 27 | await destroyExtension(); 28 | } 29 | -------------------------------------------------------------------------------- /src/lifecycle.ts: -------------------------------------------------------------------------------- 1 | import { logger } from "./logger"; 2 | import { Releases } from "./releases"; 3 | import { createActiveSession, destroySession } from "./session"; 4 | import { state } from "./state"; 5 | 6 | /** 7 | * Starts the PostgresTools extension 8 | */ 9 | export const start = async () => { 10 | state.state = "starting"; 11 | await doStart(); 12 | state.state = "started"; 13 | logger.info("PostgresTools extension started"); 14 | }; 15 | 16 | /** 17 | * Stops the PostgresTools extension 18 | */ 19 | export const stop = async () => { 20 | state.state = "stopping"; 21 | await doStop(); 22 | state.state = "stopped"; 23 | logger.info("PostgresTools extension stopped"); 24 | }; 25 | 26 | export const restart = async () => { 27 | if (state.state === "restarting") { 28 | // If we are already restarting, we can skip the restart 29 | return; 30 | } 31 | 32 | state.state = "restarting"; 33 | await doStop(); 34 | await doStart(); 35 | state.state = "started"; 36 | logger.info("PostgresTools extension restarted"); 37 | }; 38 | 39 | const doStart = async () => { 40 | try { 41 | state.releases = await Releases.load(); 42 | await createActiveSession(); 43 | } catch (e: unknown) { 44 | if (e instanceof Error) { 45 | logger.error(e.message); 46 | } 47 | logger.error("Failed to start PostgresTools extension", { error: e }); 48 | state.state = "error"; 49 | } 50 | }; 51 | 52 | const doStop = async () => { 53 | // If we end up here following a configuration change, we need to wait 54 | // for the notification to be processed before we can stop the LSP session, 55 | // otherwise we will get an error. This is a workaround for a race condition 56 | // that occurs when the configuration change notification is sent while the 57 | // LSP session is already stopped. 58 | await new Promise((resolve) => setTimeout(resolve, 1000)); 59 | 60 | if (state.activeSession) { 61 | destroySession(state.activeSession); 62 | state.activeSession = undefined; 63 | } 64 | }; 65 | -------------------------------------------------------------------------------- /src/logger.ts: -------------------------------------------------------------------------------- 1 | import { LogLevel, window } from "vscode"; 2 | import { CONSTANTS } from "./constants"; 3 | 4 | type LogArguments = Record; 5 | 6 | /** 7 | * Messages logged to this logger will be displayed in the `PostgresTools` output 8 | * channel in the Output panel. This logger respects the user's settings for 9 | * logging verbosity, so only messages with the appropriate log level will be 10 | * displayed. 11 | */ 12 | class Logger { 13 | private output = window.createOutputChannel( 14 | `${CONSTANTS.displayName} (${CONSTANTS.activationTimestamp})`, 15 | { 16 | log: true, 17 | } 18 | ); 19 | 20 | private log( 21 | message: string, 22 | level: LogLevel = LogLevel.Info, 23 | args?: LogArguments 24 | ) { 25 | if (args) { 26 | message += `\n\t${Object.entries(args) 27 | .map(([key, value]) => `${key}=${JSON.stringify(value)}`) 28 | .join("\n\t")}`; 29 | } 30 | 31 | switch (level) { 32 | case LogLevel.Error: 33 | return this.output.error(message); 34 | case LogLevel.Warning: 35 | return this.output.warn(message); 36 | case LogLevel.Info: 37 | return this.output.info(message); 38 | case LogLevel.Debug: 39 | return this.output.debug(message); 40 | default: 41 | return this.output.debug(message); 42 | } 43 | } 44 | 45 | public error(message: string, args?: LogArguments) { 46 | this.log(message, LogLevel.Error, args); 47 | } 48 | public warn(message: string, args?: LogArguments) { 49 | this.log(message, LogLevel.Warning, args); 50 | } 51 | public info(message: string, args?: LogArguments) { 52 | this.log(message, LogLevel.Info, args); 53 | } 54 | public debug(message: string, args?: LogArguments) { 55 | this.log(message, LogLevel.Debug, args); 56 | } 57 | 58 | /** 59 | * Clears the logger 60 | * 61 | * This function does not actually clear the logger, but rather appends a 62 | * few newlines to the logger to ensure that the logger so that logs from a 63 | * previous run are visually separated from the current run. We need to do 64 | * this because of a bug in VS Code where the output channel is not cleared 65 | * properly when calling `clear()` on it. 66 | * 67 | * @see https://github.com/microsoft/vscode/issues/224516 68 | */ 69 | clear() { 70 | this.output.append("\n\n\n\n\n"); 71 | } 72 | } 73 | 74 | export const logger = new Logger(); 75 | -------------------------------------------------------------------------------- /src/project.ts: -------------------------------------------------------------------------------- 1 | import { Uri, type WorkspaceFolder, window, workspace } from "vscode"; 2 | import { fileExists } from "./utils"; 3 | import { getConfig } from "./config"; 4 | import { logger } from "./logger"; 5 | 6 | export type Project = { 7 | folder?: WorkspaceFolder; 8 | path: Uri; 9 | configPath: Uri; 10 | }; 11 | 12 | export async function getActiveProject(): Promise { 13 | const folders = workspace.workspaceFolders; 14 | 15 | if (!folders?.length) { 16 | logger.warn(`No workspace folders. Single-file Mode?`); 17 | return null; 18 | } 19 | 20 | if (folders.length > 1) { 21 | window.showErrorMessage( 22 | "PostgresTools does not support Multi-Root workspace mode for now." 23 | ); 24 | return null; 25 | } 26 | 27 | return getActiveProjectForSingleFolder(folders[0]); 28 | } 29 | 30 | async function getActiveProjectForSingleFolder( 31 | first: WorkspaceFolder 32 | ): Promise { 33 | let configPath: Uri; 34 | 35 | const userConfig = getConfig("configFile", { scope: first.uri }); 36 | if (userConfig) { 37 | logger.info("User has specified path to config file.", { 38 | path: userConfig, 39 | }); 40 | configPath = Uri.joinPath(first.uri, userConfig); 41 | } else { 42 | logger.info("User did not specify path to config file. Using default."); 43 | configPath = Uri.joinPath(first.uri, "postgrestools.jsonc"); 44 | } 45 | 46 | if (!(await fileExists(configPath))) { 47 | logger.info("Config file does not exist.", { 48 | path: configPath.fsPath, 49 | }); 50 | return null; 51 | } else { 52 | logger.info("Found config file.", { 53 | path: configPath.fsPath, 54 | }); 55 | } 56 | 57 | return { 58 | folder: first, 59 | path: first.uri, 60 | configPath, 61 | }; 62 | } 63 | -------------------------------------------------------------------------------- /src/releases.ts: -------------------------------------------------------------------------------- 1 | import { window } from "vscode"; 2 | import { logger } from "./logger"; 3 | import { getConfig } from "./config"; 4 | import { state } from "./state"; 5 | import { daysToMs } from "./utils"; 6 | 7 | export type Release = { 8 | tag_name: string; 9 | published_at: string; 10 | draft: boolean; 11 | prerelease: boolean; 12 | }; 13 | 14 | const CACHE_KEY = "releases"; 15 | 16 | type Cached = { 17 | cachedAt: string; 18 | releases: Release[]; 19 | }; 20 | 21 | async function setCache(releases: Release[] | null) { 22 | await state.context.globalState.update( 23 | CACHE_KEY, 24 | releases 25 | ? ({ 26 | releases, 27 | cachedAt: new Date().toISOString(), 28 | } satisfies Cached) 29 | : undefined 30 | ); 31 | } 32 | 33 | async function fromCache(): Promise { 34 | logger.debug(`Searching for releases in cache.`); 35 | const cached = state.context.globalState.get(CACHE_KEY); 36 | 37 | if (!cached) { 38 | logger.debug(`No cached releases found.`); 39 | await setCache(null); 40 | return null; 41 | } 42 | 43 | if (Date.now() - new Date(cached.cachedAt).getTime() >= daysToMs(3)) { 44 | logger.debug(`Stale cached releases found. Invalidating.`); 45 | await setCache(null); 46 | return null; 47 | } 48 | 49 | return cached.releases; 50 | } 51 | 52 | export class Releases { 53 | private constructor(private releases: Release[]) {} 54 | 55 | static async load() { 56 | const releases = 57 | (await fromCache()) || (await fromGithub().catch(() => [])); 58 | 59 | logger.debug(`Found ${releases.length} downloadable versions`, { 60 | prereleases: releases.filter((r) => r.prerelease).length, 61 | }); 62 | 63 | return new Releases(releases); 64 | } 65 | 66 | static async refresh() { 67 | await setCache(null); 68 | } 69 | 70 | all(): Readonly[] { 71 | if (this.prereleaseEnabled()) { 72 | return this.releases; 73 | } else { 74 | return this.releases.filter((r) => !r.prerelease); 75 | } 76 | } 77 | 78 | versionOutdated(tag_name: string) { 79 | if (!(getConfig("allowVersionChecks") ?? true)) { 80 | return false; 81 | } 82 | 83 | const prereleaseEnabled = this.prereleaseEnabled(); 84 | 85 | const idx = this.releases 86 | .filter((r) => prereleaseEnabled || !r.prerelease) 87 | .findIndex((r) => r.tag_name === tag_name); 88 | 89 | if (idx === -1) { 90 | return true; 91 | } 92 | 93 | if (idx >= 3) { 94 | return true; 95 | } 96 | } 97 | 98 | latestVersion(): string | null { 99 | if (this.releases.length > 0) { 100 | return this.releases[0].tag_name; 101 | } else { 102 | return null; 103 | } 104 | } 105 | 106 | private prereleaseEnabled() { 107 | const withPrereleases = 108 | getConfig("allowDownloadPrereleases") ?? false; 109 | 110 | return withPrereleases; 111 | } 112 | } 113 | 114 | async function fromGithub(): Promise { 115 | logger.debug("Fetching releases from GitHub API."); 116 | 117 | let page = 1; 118 | let perPage = 100; 119 | let exhausted = false; 120 | 121 | const releases = []; 122 | 123 | while (!exhausted) { 124 | const queryParams = new URLSearchParams(); 125 | 126 | queryParams.append("page", page.toString()); 127 | queryParams.append("per_page", perPage.toString()); 128 | 129 | const response = await fetch( 130 | `https://api.github.com/repos/supabase-community/postgres_lsp/releases?${queryParams.toString()}`, 131 | { 132 | method: "GET", 133 | headers: { 134 | "X-GitHub-Api-Version": "2022-11-28", 135 | }, 136 | } 137 | ); 138 | 139 | if (!response.ok) { 140 | const body = await response.json(); 141 | window.showErrorMessage( 142 | `Could not fetch releases from GitHub! Received Status Code: ${response.status}, Body: ${body}` 143 | ); 144 | return []; 145 | } 146 | 147 | const results = (await response.json()) as Release[]; 148 | 149 | if (results.length === 0) { 150 | window.showErrorMessage( 151 | 'No releases found on GitHub. Suggestion: Set "postgrestools.allowDownloadPrereleases" to `true` in your vscode settings.' 152 | ); 153 | return []; 154 | } 155 | 156 | releases.push(...results); 157 | 158 | if (page > 30) { 159 | // sanity 160 | exhausted = true; 161 | } else if (results.length < perPage) { 162 | exhausted = true; 163 | } else { 164 | page++; 165 | } 166 | } 167 | 168 | const filteredAndSorted = releases 169 | .filter((r) => !r.draft) 170 | .sort( 171 | (a, b) => 172 | new Date(b.published_at).getTime() - new Date(a.published_at).getTime() 173 | ); 174 | 175 | await setCache(filteredAndSorted); 176 | 177 | return filteredAndSorted; 178 | } 179 | -------------------------------------------------------------------------------- /src/session.ts: -------------------------------------------------------------------------------- 1 | import { spawnSync } from "node:child_process"; 2 | import { chmodSync, copyFileSync } from "node:fs"; 3 | import { type LogOutputChannel, Uri, window, workspace } from "vscode"; 4 | import { 5 | CloseAction, 6 | type CloseHandlerResult, 7 | type DocumentFilter, 8 | ErrorAction, 9 | type ErrorHandlerResult, 10 | type InitializeParams, 11 | LanguageClient, 12 | type LanguageClientOptions, 13 | type ServerOptions, 14 | TransportKind, 15 | } from "vscode-languageclient/node"; 16 | import { BinaryFinder } from "./binary-finder"; 17 | import { logger } from "./logger"; 18 | import { getActiveProject, type Project } from "./project"; 19 | import { state } from "./state"; 20 | import { 21 | daysToMs, 22 | fileExists, 23 | fileIsExecutable, 24 | getVersion, 25 | subtractURI, 26 | } from "./utils"; 27 | import { CONSTANTS, OperatingMode } from "./constants"; 28 | import { getConfig, isEnabledForFolder } from "./config"; 29 | 30 | export type Session = { 31 | bin: Uri; 32 | binaryStrategyLabel: string; 33 | tempBin?: Uri; 34 | project?: Project; 35 | client: LanguageClient; 36 | }; 37 | 38 | /** 39 | * Creates a new Pglt LSP session 40 | */ 41 | export const createSession = async ( 42 | project: Project 43 | ): Promise => { 44 | const findResult = await BinaryFinder.find(project.path); 45 | 46 | if (!findResult) { 47 | window.showErrorMessage( 48 | `Unable to find a PostgresTools binary. Read the docs for more various strategies to install a binary.` 49 | ); 50 | logger.error("Could not find the PostgresTools binary"); 51 | return; 52 | } 53 | 54 | if (!fileIsExecutable(findResult.bin)) { 55 | window.showErrorMessage(` 56 | The binary you've pointed to is not executable. 57 | (${findResult.bin.fsPath}) 58 | `); 59 | logger.error("Found binary is not executable."); 60 | return; 61 | } 62 | 63 | logger.info("Copying binary to temp location", { 64 | currentLocation: findResult.bin.fsPath, 65 | }); 66 | 67 | const version = await getVersion(findResult.bin); 68 | if (!version) { 69 | throw new Error( 70 | "Just verified we have an executable file. Version should exist." 71 | ); 72 | } 73 | 74 | const lastNotifiedOfUpdate = 75 | state.context.globalState.get("lastNotifiedOfUpdate") || 76 | new Date(0).toISOString(); 77 | 78 | if ( 79 | state.releases.versionOutdated(version) && 80 | state.releases.latestVersion() && 81 | Date.now() - new Date(lastNotifiedOfUpdate).getTime() > daysToMs(3) 82 | ) { 83 | window.showInformationMessage( 84 | `PostgresTools ${version} is outdated, consider updating to ${state.releases.latestVersion()}.` 85 | ); 86 | await state.context.globalState.update( 87 | "lastNotifiedOfUpdate", 88 | new Date().toISOString() 89 | ); 90 | } 91 | 92 | // Copy the binary to a temporary location, and run it from there 93 | // so that the original binary can be updated without locking issues. 94 | // We'll keep track of that temporary location in the session and 95 | // delete it when the session is stopped. 96 | const tempBin = await copyBinaryToTemporaryLocation(findResult.bin); 97 | 98 | if (!tempBin) { 99 | logger.warn("Failed to copy binary to temporary location. Using original."); 100 | } 101 | 102 | return { 103 | bin: findResult.bin, 104 | tempBin: tempBin, 105 | project, 106 | binaryStrategyLabel: findResult.label, 107 | client: createLanguageClient(tempBin ?? findResult.bin, project), 108 | }; 109 | }; 110 | 111 | export const destroySession = async (session: Session) => { 112 | // Stop the LSP client if it is still running 113 | if (session.client.needsStop()) { 114 | await session.client.stop(); 115 | } 116 | }; 117 | 118 | /** 119 | * Copies the binary to a temporary location if necessary 120 | * 121 | * This function will copy the binary to a temporary location if it is not already 122 | * present in the global storage directory. It will then return the location of 123 | * the copied binary. 124 | * 125 | * This approach allows the user to update the original binary that would otherwise 126 | * be locked if we ran the binary directly from the original location. 127 | * 128 | * Binaries copied in the temp location are uniquely identified by their name and version 129 | * identifier. 130 | */ 131 | const copyBinaryToTemporaryLocation = async ( 132 | bin: Uri 133 | ): Promise => { 134 | const version = await getVersion(bin); 135 | if (!version) { 136 | window.showErrorMessage( 137 | "Tried to copy binary to temporary location, but it does not exist. Invalid state." 138 | ); 139 | } 140 | 141 | logger.debug(`Retrieved version from binary`, { version }); 142 | 143 | const location = Uri.joinPath( 144 | state.context.globalStorageUri, 145 | "tmp-bin", 146 | CONSTANTS.platformSpecificBinaryName.replace( 147 | "postgrestools", 148 | `postgrestools-${version}` 149 | ) 150 | ); 151 | 152 | try { 153 | await workspace.fs.createDirectory( 154 | Uri.joinPath(state.context.globalStorageUri, "tmp-bin") 155 | ); 156 | 157 | if (!(await fileExists(location))) { 158 | logger.info("Copying binary to temporary location.", { 159 | original: bin.fsPath, 160 | destination: location.fsPath, 161 | }); 162 | copyFileSync(bin.fsPath, location.fsPath); 163 | logger.debug( 164 | "Copied postgrestools binary binary to temporary location.", 165 | { 166 | original: bin.fsPath, 167 | temporary: location.fsPath, 168 | } 169 | ); 170 | } else { 171 | logger.debug( 172 | `A postgrestools binary for the same version ${version} already exists in the temporary location.`, 173 | { 174 | original: bin.fsPath, 175 | temporary: location.fsPath, 176 | } 177 | ); 178 | } 179 | 180 | const isExecutableBefore = fileIsExecutable(bin); 181 | chmodSync(location.fsPath, 0o755); 182 | const isExecutableAfter = fileIsExecutable(bin); 183 | 184 | logger.debug("Ensure binary is executable", { 185 | binary: bin.fsPath, 186 | before: `is executable: ${isExecutableBefore}`, 187 | after: `is executable: ${isExecutableAfter}`, 188 | }); 189 | 190 | return location; 191 | } catch (error) { 192 | logger.warn(`Error copying binary: ${error}`); 193 | } 194 | }; 195 | 196 | /** 197 | * Creates a new global session 198 | */ 199 | export const createActiveSession = async () => { 200 | if (state.activeSession) { 201 | return; 202 | } 203 | 204 | const activeProject = await getActiveProject(); 205 | 206 | if (!activeProject) { 207 | logger.info("No active project found. Aborting."); 208 | return; 209 | } 210 | 211 | if (activeProject.folder && !isEnabledForFolder(activeProject.folder)) { 212 | logger.info("Extension disabled for project."); 213 | return; 214 | } 215 | 216 | if (!activeProject.folder && !getConfig("enabled")) { 217 | logger.info("Extension disabled."); 218 | return; 219 | } 220 | 221 | state.activeProject = activeProject; 222 | state.activeSession = await createSession(activeProject); 223 | 224 | try { 225 | await state.activeSession?.client.start(); 226 | logger.info("Created a global LSP session"); 227 | } catch (e) { 228 | logger.error("Failed to create global LSP session", { 229 | error: `${e}`, 230 | }); 231 | state.activeSession?.client.dispose(); 232 | state.activeSession = undefined; 233 | } 234 | }; 235 | 236 | /** 237 | * Creates a new PostgresTools LSP client 238 | */ 239 | const createLanguageClient = (bin: Uri, project: Project) => { 240 | const args = ["lsp-proxy", `--config-path=${project.configPath.fsPath}`]; 241 | 242 | const serverOptions: ServerOptions = { 243 | command: bin.fsPath, 244 | transport: TransportKind.stdio, 245 | options: { 246 | ...(project?.path && { cwd: project.path.fsPath }), 247 | }, 248 | args, 249 | }; 250 | 251 | logger.info(`Server Options: `, { 252 | serverOptions, 253 | }); 254 | 255 | const clientOptions: LanguageClientOptions = { 256 | outputChannel: createLspLogger(project), 257 | traceOutputChannel: createLspTraceLogger(project), 258 | documentSelector: createDocumentSelector(project), 259 | progressOnInitialization: true, 260 | 261 | initializationFailedHandler: (e): boolean => { 262 | logger.error("Failed to initialize the PostgresTools language server", { 263 | error: e.toString(), 264 | }); 265 | 266 | return false; 267 | }, 268 | errorHandler: { 269 | error: ( 270 | error, 271 | message, 272 | count 273 | ): ErrorHandlerResult | Promise => { 274 | logger.error("PostgresTools language server error", { 275 | error: error.toString(), 276 | stack: error.stack, 277 | errorMessage: error.message, 278 | message: message?.jsonrpc, 279 | count: count, 280 | }); 281 | 282 | return { 283 | action: ErrorAction.Shutdown, 284 | message: "PostgresTools language server error", 285 | }; 286 | }, 287 | closed: (): CloseHandlerResult | Promise => { 288 | logger.error("PostgresTools language server closed"); 289 | return { 290 | action: CloseAction.DoNotRestart, 291 | message: "PostgresTools language server closed", 292 | }; 293 | }, 294 | }, 295 | initializationOptions: { 296 | rootUri: project?.path, 297 | rootPath: project?.path?.fsPath, 298 | }, 299 | workspaceFolder: undefined, 300 | }; 301 | 302 | return new PostgresToolsLanguageClient( 303 | "postgrestools.lsp", 304 | "postgrestools", 305 | serverOptions, 306 | clientOptions 307 | ); 308 | }; 309 | 310 | /** 311 | * Creates a new PostgresTools LSP logger 312 | */ 313 | const createLspLogger = (project?: Project): LogOutputChannel => { 314 | // If the project is missing, we're creating a logger for the global LSP 315 | // session. In this case, we don't have a workspace folder to display in the 316 | // logger name, so we just use the display name of the extension. 317 | if (!project?.folder) { 318 | return window.createOutputChannel( 319 | `${CONSTANTS.displayName} LSP (global session) (${CONSTANTS.activationTimestamp})`, 320 | { 321 | log: true, 322 | } 323 | ); 324 | } 325 | 326 | // If the project is present, we're creating a logger for a specific project. 327 | // In this case, we display the name of the project and the relative path to 328 | // the project root in the logger name. Additionally, when in a multi-root 329 | // workspace, we prefix the path with the name of the workspace folder. 330 | const prefix = 331 | CONSTANTS.operatingMode === OperatingMode.MultiRoot 332 | ? `${project.folder.name}::` 333 | : ""; 334 | const path = subtractURI(project.path, project.folder.uri)?.fsPath; 335 | 336 | return window.createOutputChannel( 337 | `${CONSTANTS.displayName} LSP (${prefix}${path}) (${CONSTANTS.activationTimestamp})`, 338 | { 339 | log: true, 340 | } 341 | ); 342 | }; 343 | 344 | /** 345 | * Creates a new PostgresTools LSP logger 346 | */ 347 | const createLspTraceLogger = (project?: Project): LogOutputChannel => { 348 | // If the project is missing, we're creating a logger for the global LSP 349 | // session. In this case, we don't have a workspace folder to display in the 350 | // logger name, so we just use the display name of the extension. 351 | if (!project?.folder) { 352 | return window.createOutputChannel( 353 | `${CONSTANTS.displayName} LSP trace (global session) (${CONSTANTS.activationTimestamp})`, 354 | { 355 | log: true, 356 | } 357 | ); 358 | } 359 | 360 | // If the project is present, we're creating a logger for a specific project. 361 | // In this case, we display the name of the project and the relative path to 362 | // the project root in the logger name. Additionally, when in a multi-root 363 | // workspace, we prefix the path with the name of the workspace folder. 364 | const prefix = 365 | CONSTANTS.operatingMode === OperatingMode.MultiRoot 366 | ? `${project.folder.name}::` 367 | : ""; 368 | const path = subtractURI(project.path, project.folder.uri)?.fsPath; 369 | 370 | return window.createOutputChannel( 371 | `${CONSTANTS.displayName} LSP trace (${prefix}${path}) (${CONSTANTS.activationTimestamp})`, 372 | { 373 | log: true, 374 | } 375 | ); 376 | }; 377 | 378 | /** 379 | * Creates a new document selector 380 | * 381 | * This function will create a document selector scoped to the given project, 382 | * which will only match files within the project's root directory. If no 383 | * project is specified, the document selector will match files that have 384 | * not yet been saved to disk (untitled). 385 | */ 386 | const createDocumentSelector = (project?: Project): DocumentFilter[] => { 387 | return ["sql", "postgres"].flatMap((language) => { 388 | if (project) { 389 | return { 390 | language, 391 | scheme: "file", 392 | pattern: Uri.joinPath(project.path, "**", "*").fsPath.replaceAll( 393 | "\\", 394 | "/" 395 | ), 396 | }; 397 | } 398 | 399 | return ["untitled", "vscode-userdata"].map((scheme) => ({ 400 | language, 401 | scheme, 402 | })); 403 | }); 404 | }; 405 | 406 | class PostgresToolsLanguageClient extends LanguageClient { 407 | protected fillInitializeParams(params: InitializeParams): void { 408 | super.fillInitializeParams(params); 409 | 410 | if (params.initializationOptions?.rootUri) { 411 | params.rootUri = params.initializationOptions?.rootUri.toString(); 412 | } 413 | 414 | if (params.initializationOptions?.rootPath) { 415 | params.rootPath = params.initializationOptions?.rootPath; 416 | } 417 | } 418 | } 419 | -------------------------------------------------------------------------------- /src/state.ts: -------------------------------------------------------------------------------- 1 | import { ExtensionContext } from "vscode"; 2 | import { Releases } from "./releases"; 3 | import { Project } from "./project"; 4 | import { Session } from "./session"; 5 | 6 | export type State = { 7 | state: 8 | | "initializing" 9 | | "starting" 10 | | "restarting" 11 | | "started" 12 | | "stopping" 13 | | "stopped" 14 | | "error"; 15 | 16 | activeProject?: Project; 17 | activeSession?: Session; 18 | context: ExtensionContext; 19 | hidden: boolean; 20 | releases: Releases; 21 | }; 22 | 23 | const _state: State = { 24 | state: "initializing", 25 | hidden: false, 26 | } as State; 27 | 28 | export const state = new Proxy(_state, { 29 | get(target, prop, receiver) { 30 | return Reflect.get(target, prop, receiver); 31 | }, 32 | set(target, prop, value, receiver) { 33 | if (Reflect.set(target, prop, value, receiver)) { 34 | // update something 35 | return true; 36 | } 37 | 38 | return false; 39 | }, 40 | }); 41 | -------------------------------------------------------------------------------- /src/status-bar.ts: -------------------------------------------------------------------------------- 1 | import { 2 | StatusBarAlignment, 3 | type StatusBarItem, 4 | TextEditor, 5 | window, 6 | } from "vscode"; 7 | import { type State, state } from "./state"; 8 | import { isEnabledForFolder } from "./config"; 9 | 10 | export type StatusBar = { 11 | item: StatusBarItem; 12 | }; 13 | 14 | const createStatusBar = (): StatusBar => { 15 | const item = window.createStatusBarItem( 16 | "postgrestools_vscode", 17 | StatusBarAlignment.Right, 18 | 1 19 | ); 20 | 21 | return { item }; 22 | }; 23 | 24 | export const updateStatusBar = () => { 25 | if (!state) { 26 | return; 27 | } 28 | 29 | const enabled = 30 | state.activeProject?.folder && 31 | isEnabledForFolder(state.activeProject.folder); 32 | 33 | if (!enabled || state.hidden) { 34 | statusBar.item.hide(); 35 | return; 36 | } 37 | 38 | const icon = getStateIcon(state); 39 | const text = getStateText(); 40 | const version = getLspVersion(); 41 | const tooltip = getStateTooltip(); 42 | 43 | statusBar.item.text = `${icon} ${text} ${version}`.trim(); 44 | statusBar.item.tooltip = tooltip; 45 | statusBar.item.show(); 46 | }; 47 | 48 | export const updateHidden = (editor: TextEditor | undefined) => { 49 | state.hidden = 50 | editor?.document === undefined || 51 | !["sql", "postgres"].includes(editor.document.languageId); 52 | }; 53 | 54 | const getLspVersion = () => { 55 | return ( 56 | state.activeSession?.client.initializeResult?.serverInfo?.version ?? "" 57 | ); 58 | }; 59 | 60 | const getStateText = (): string => { 61 | return "PostgresTools"; 62 | }; 63 | 64 | const getStateTooltip = (): string => { 65 | switch (state.state) { 66 | case "initializing": 67 | return "Initializing"; 68 | case "starting": 69 | return "Starting"; 70 | case "restarting": 71 | return "Restarting"; 72 | case "started": 73 | return "Up and running"; 74 | case "stopping": 75 | return "Stopping"; 76 | case "stopped": 77 | return "Stopped"; 78 | case "error": 79 | return "Error"; 80 | } 81 | }; 82 | 83 | const getStateIcon = (state: State): string => { 84 | switch (state.state) { 85 | case "initializing": 86 | return "$(sync~spin)"; 87 | case "starting": 88 | return "$(sync~spin)"; 89 | case "restarting": 90 | return "$(sync~spin)"; 91 | case "started": 92 | return "$(check)"; 93 | case "stopping": 94 | return "$(sync~spin)"; 95 | case "stopped": 96 | return "$(x)"; 97 | case "error": 98 | return "$(error)"; 99 | default: 100 | return "$(question)"; 101 | } 102 | }; 103 | 104 | export const statusBar = createStatusBar(); 105 | -------------------------------------------------------------------------------- /src/utils.ts: -------------------------------------------------------------------------------- 1 | import { FileType, Uri, workspace } from "vscode"; 2 | import { logger } from "./logger"; 3 | import { state } from "./state"; 4 | import { CONSTANTS } from "./constants"; 5 | import { accessSync, constants } from "node:fs"; 6 | import { spawnSync } from "node:child_process"; 7 | 8 | export function debounce( 9 | fn: (...args: TArgs) => void, 10 | delay = 300 11 | ) { 12 | let timeout: NodeJS.Timeout | undefined; 13 | return (...args: TArgs) => { 14 | clearTimeout(timeout); 15 | setTimeout(() => fn(...args), delay); 16 | }; 17 | } 18 | 19 | export async function fileExists(uri: Uri): Promise { 20 | try { 21 | const result = await workspace.fs.stat(uri); 22 | 23 | /** the file can also be a symlink, hence the bitwise operation */ 24 | return (result.type & FileType.File) > 0; 25 | } catch (err: unknown) { 26 | if (err instanceof Error && err.message.includes("ENOENT")) { 27 | return false; 28 | } else { 29 | logger.debug(`Error verifying if file exists, uri: ${uri}, err: ${err}`); 30 | return false; 31 | } 32 | } 33 | } 34 | 35 | export async function dirExists(uri: Uri): Promise { 36 | try { 37 | const result = await workspace.fs.stat(uri); 38 | 39 | /** the file can also be a symlink, hence the bitwise operation */ 40 | return (result.type & FileType.Directory) > 0; 41 | } catch (err: unknown) { 42 | logger.debug(`Error verifying if dir exists, uri: ${uri}, err: ${err}`); 43 | return false; 44 | } 45 | } 46 | 47 | /** 48 | * This function clears any temporary binaries that may have been created by 49 | * the extension. It deletes the `CONSTANTS.globalStorageFolderTmp` directory within the global storage 50 | * directory. 51 | */ 52 | export async function clearTemporaryBinaries() { 53 | logger.debug("Clearing temporary binaries"); 54 | 55 | const binDirPath = Uri.joinPath( 56 | state.context.globalStorageUri, 57 | CONSTANTS.globalStorageFolderTmp 58 | ); 59 | 60 | if (await dirExists(binDirPath)) { 61 | workspace.fs.delete(binDirPath, { 62 | recursive: true, 63 | }); 64 | logger.debug("Cleared temporary binaries.", { 65 | path: binDirPath.fsPath, 66 | }); 67 | } 68 | } 69 | 70 | export async function clearGlobalBinaries() { 71 | logger.debug("Clearing global binaries"); 72 | 73 | const binDirPath = Uri.joinPath( 74 | state.context.globalStorageUri, 75 | CONSTANTS.globalStorageFolderForBinary 76 | ); 77 | 78 | if (await dirExists(binDirPath)) { 79 | workspace.fs.delete(binDirPath, { 80 | recursive: true, 81 | }); 82 | logger.debug("Cleared global binaries.", { 83 | path: binDirPath.fsPath, 84 | }); 85 | } 86 | } 87 | 88 | /** 89 | * Substracts the second string from the first string 90 | */ 91 | export function subtractURI(original: Uri, subtract: Uri): Uri | undefined { 92 | const _original = original.fsPath; 93 | const _subtract = subtract.fsPath; 94 | 95 | let result = _original.replace(_subtract, ""); 96 | 97 | result = result === "" ? "/" : result; 98 | 99 | return Uri.parse(result); 100 | } 101 | 102 | /** 103 | * Checks if a file is executable 104 | * 105 | * This function checks if a file is executable using Node's `accessSync` function. 106 | * It returns true if the file is executable, otherwise it returns false. 107 | * 108 | * This is used to ensure that downloaded PostgresTools binaries are executable. 109 | */ 110 | export function fileIsExecutable(uri: Uri): boolean { 111 | try { 112 | accessSync(uri.fsPath, constants.X_OK); 113 | return true; 114 | } catch { 115 | return false; 116 | } 117 | } 118 | 119 | // Retrieve the version of the binary 120 | // We call postgrestools with --version which outputs the version in the format 121 | // of "Version: 1.0.0" 122 | export async function getVersion(bin: Uri): Promise { 123 | if (!(await fileExists(bin))) { 124 | return null; 125 | } 126 | 127 | return spawnSync(bin.fsPath, ["--version"]) 128 | .stdout.toString() 129 | .split(":")[1] 130 | .trim(); 131 | } 132 | 133 | export function daysToMs(days: number) { 134 | return days * 24 * 60 * 60 * 1000; 135 | } 136 | -------------------------------------------------------------------------------- /test/fixtures/GLOBAL_SETUP.md: -------------------------------------------------------------------------------- 1 | # Setting Up a Test 2 | 3 | 1. Run the `docker-compose.yml` file. 4 | 2. Optional: If you want to work on the extension while debugging, run `npm run watch`. You'll see changes if you use the "Restart Extension Host" command in the debugging window. 5 | 3. Run `just init` (install `just` if you haven't, or use the shell command in the `justfile`). 6 | 4. Open the VSCode Debug window and choose the appropriate launch configuration. Press F5. 7 | 8 | ## Getting a Fresh State 9 | 10 | In the extension host, follow the test's TestSetup in the relevant README. 11 | Before the test assertions, use the "PostgresTools: Hard Reset" VSCode command. This will clear all previously installed binaries. 12 | -------------------------------------------------------------------------------- /test/fixtures/monorepo-nested-config/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "postgrestools.configFile": "/packages/service-a/postgrestools.jsonc", 3 | "postgrestools.allowDownloadPrereleases": true 4 | } 5 | -------------------------------------------------------------------------------- /test/fixtures/monorepo-nested-config/README.md: -------------------------------------------------------------------------------- 1 | # Simple Project 2 | 3 | A mono repo that contains top-level dotfiles and several services in the `packages` folder. 4 | 5 | ## Expectations 6 | 7 | The extension should recognize the `.vscode/settings.json` file and follow it to the `postgrestools.jsonc` file in the `packages/service-a` directory. 8 | 9 | ## Test protocol 10 | 11 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 12 | 1. You should be prompted to download a file once you open the extension host. 13 | 2. Once downloaded, the extension should work as expected when you open the `packages/service-b/test.sql` file. 14 | -------------------------------------------------------------------------------- /test/fixtures/monorepo-nested-config/packages/service-a/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/monorepo-nested-config/packages/service-b/test.sql: -------------------------------------------------------------------------------- 1 | select * from users; -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/.gitignore: -------------------------------------------------------------------------------- 1 | node_modules/ -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/README.md: -------------------------------------------------------------------------------- 1 | # Simple Project 2 | 3 | A bread-and-butter project with a `postgrestools.jsonc` on top level and SQL files in the `test` folder. The extension is installed via node_modules. 4 | 5 | ## Expectations 6 | 7 | The extension should recognize the `postgrestools.jsonc` file and connect with the right database. We shouldn't have to install a global binary. 8 | 9 | ## Test protocol 10 | 11 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 12 | 1. Run `npm install`. 13 | 2. You shouldn't be prompted to download a file once you open the extension host. 14 | 3. The extension should work as expected when you open the `src/test.sql` file. 15 | -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/package-lock.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "lockfileVersion": 3, 4 | "requires": true, 5 | "packages": { 6 | "": { 7 | "name": "my-project", 8 | "devDependencies": { 9 | "@postgrestools/postgrestools": "0.4.0" 10 | } 11 | }, 12 | "node_modules/@postgrestools/cli-aarch64-apple-darwin": { 13 | "version": "0.4.0", 14 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-aarch64-apple-darwin/-/cli-aarch64-apple-darwin-0.4.0.tgz", 15 | "integrity": "sha512-L+kitPI2tTIdJiA0bFRg2h1Eyhd5OHbs7cyDrDXfZMq49CLE8iAEWln9W1u45Uk2kLCyRCaqMn7WYfV0pVCciw==", 16 | "cpu": [ 17 | "arm64" 18 | ], 19 | "dev": true, 20 | "license": "MIT or Apache-2.0", 21 | "optional": true, 22 | "os": [ 23 | "darwin" 24 | ], 25 | "engines": { 26 | "node": ">=20" 27 | } 28 | }, 29 | "node_modules/@postgrestools/cli-aarch64-linux-gnu": { 30 | "version": "0.4.0", 31 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-aarch64-linux-gnu/-/cli-aarch64-linux-gnu-0.4.0.tgz", 32 | "integrity": "sha512-ZXi4aY4NzkugTBJa2LFn1YZYPiu0vrxp6+1auipAdsK1N1tmCMbGXDvjZgaUyadAFpxRDTpeJXrSLeiZwyibsg==", 33 | "cpu": [ 34 | "arm64" 35 | ], 36 | "dev": true, 37 | "license": "MIT or Apache-2.0", 38 | "optional": true, 39 | "os": [ 40 | "linux" 41 | ], 42 | "engines": { 43 | "node": ">=20" 44 | } 45 | }, 46 | "node_modules/@postgrestools/cli-aarch64-windows-msvc": { 47 | "version": "0.4.0", 48 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-aarch64-windows-msvc/-/cli-aarch64-windows-msvc-0.4.0.tgz", 49 | "integrity": "sha512-9r9LJvmMyloMfvCwDmjWWw/8aETIH/epmVBxMRvuPoDf/XTTbrgz5K5qMaN7OFJql4fwK0m1eL8JsiXlssFr8g==", 50 | "cpu": [ 51 | "arm64" 52 | ], 53 | "dev": true, 54 | "license": "MIT or Apache-2.0", 55 | "optional": true, 56 | "os": [ 57 | "win32" 58 | ], 59 | "engines": { 60 | "node": ">=20" 61 | } 62 | }, 63 | "node_modules/@postgrestools/cli-x86_64-apple-darwin": { 64 | "version": "0.4.0", 65 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-x86_64-apple-darwin/-/cli-x86_64-apple-darwin-0.4.0.tgz", 66 | "integrity": "sha512-2MMJr87nF+TZ6T44xMp/uZygPXKcmEuuRFFrcnFXp4ov2fmjapz2JC0w0v2TdIAhz9nL6cNkVHIxED8AH724XA==", 67 | "cpu": [ 68 | "x64" 69 | ], 70 | "dev": true, 71 | "license": "MIT or Apache-2.0", 72 | "optional": true, 73 | "os": [ 74 | "darwin" 75 | ], 76 | "engines": { 77 | "node": ">=20" 78 | } 79 | }, 80 | "node_modules/@postgrestools/cli-x86_64-linux-gnu": { 81 | "version": "0.4.0", 82 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-x86_64-linux-gnu/-/cli-x86_64-linux-gnu-0.4.0.tgz", 83 | "integrity": "sha512-v9efC1Vcq2KPgYWtZyG9nMv0soL2gpY0/Zok+wsoEZE9je5AKDMcLiLVFLzpYgUc1cO6I2KB4uvOh8ZBJUE7wQ==", 84 | "cpu": [ 85 | "x64" 86 | ], 87 | "dev": true, 88 | "license": "MIT or Apache-2.0", 89 | "optional": true, 90 | "os": [ 91 | "linux" 92 | ], 93 | "engines": { 94 | "node": ">=20" 95 | } 96 | }, 97 | "node_modules/@postgrestools/cli-x86_64-windows-msvc": { 98 | "version": "0.4.0", 99 | "resolved": "https://registry.npmjs.org/@postgrestools/cli-x86_64-windows-msvc/-/cli-x86_64-windows-msvc-0.4.0.tgz", 100 | "integrity": "sha512-w+4iFblAPpBDO7jJotwaASyt5+RdHV1fpj/rzIM9QpRSTWFs/wvTqfhetb8hMQ3YD/G4+xTdBq4nyT40m1Ptog==", 101 | "cpu": [ 102 | "x64" 103 | ], 104 | "dev": true, 105 | "license": "MIT or Apache-2.0", 106 | "optional": true, 107 | "os": [ 108 | "win32" 109 | ], 110 | "engines": { 111 | "node": ">=20" 112 | } 113 | }, 114 | "node_modules/@postgrestools/postgrestools": { 115 | "version": "0.4.0", 116 | "resolved": "https://registry.npmjs.org/@postgrestools/postgrestools/-/postgrestools-0.4.0.tgz", 117 | "integrity": "sha512-is6UlTgtAVr42ZRXdM4QmvhPvQgweVaUk0ETzOqR4hjpARxwX6F6iiRcuW5+gHnt1cXocFLKO0djHGcErOywBw==", 118 | "dev": true, 119 | "license": "MIT or Apache-2.0", 120 | "bin": { 121 | "postgrestools": "bin/postgrestools" 122 | }, 123 | "engines": { 124 | "node": ">=20" 125 | }, 126 | "optionalDependencies": { 127 | "@postgrestools/cli-aarch64-apple-darwin": "0.4.0", 128 | "@postgrestools/cli-aarch64-linux-gnu": "0.4.0", 129 | "@postgrestools/cli-aarch64-windows-msvc": "0.4.0", 130 | "@postgrestools/cli-x86_64-apple-darwin": "0.4.0", 131 | "@postgrestools/cli-x86_64-linux-gnu": "0.4.0", 132 | "@postgrestools/cli-x86_64-windows-msvc": "0.4.0" 133 | } 134 | } 135 | } 136 | } 137 | -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "devDependencies": { 4 | "@postgrestools/postgrestools": "0.4.0" 5 | } 6 | } 7 | -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/node-modules-strat/src/test.sql: -------------------------------------------------------------------------------- 1 | select 2 | * 3 | from users; -------------------------------------------------------------------------------- /test/fixtures/simple-project-download/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "postgrestools.allowDownloadPrereleases": true 3 | } 4 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-download/README.md: -------------------------------------------------------------------------------- 1 | # Simple Project 2 | 3 | A bread-and-butter project with a `postgrestools.jsonc` on top level and SQL files in the `src` folder. 4 | 5 | ## Expectations 6 | 7 | The extension should recognize the `postgrestools.jsonc` file and connect with the right database. 8 | 9 | ## Test protocol 10 | 11 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 12 | 1. You should be prompted to download a file once you open the extension host. 13 | 2. Once downloaded, the extension should work as expected when you open the `src/test.sql` file. 14 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-download/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-download/src/test.sql: -------------------------------------------------------------------------------- 1 | select e from users; -------------------------------------------------------------------------------- /test/fixtures/simple-project-global-bin/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | } 3 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-global-bin/README.md: -------------------------------------------------------------------------------- 1 | # Simple Project 2 | 3 | A bread-and-butter project with a `postgrestools.jsonc` on top level and SQL files in the `test` folder. 4 | You should either let the `.vscode/settings.json` point to a binary OR add a folder containing the `postgrestools(.exe)` binary to your `PATH`. 5 | 6 | ## Expectations 7 | 8 | The extension should recognize the `postgrestools.jsonc` file and connect with the right database. If the global binary is installed, it should not prompt for a local binary. 9 | 10 | ## Test protocol 11 | 12 | ### Via $PATH 13 | 14 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 15 | 1. Make sure the `.vscode/settings.json` file does _not_ have a `postgrestools.bin` setting. 16 | 2. Make sure you have a folder containing a `postgrestools(.exe)` binary in your $PATH. 17 | 3. You should not be prompted for any binary installations when you start the extensions. 18 | 4. The extension should work when you open the `src/test.sql` file. 19 | 20 | ### Via VSCode Settings 21 | 22 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 23 | 1. Make sure you have a valid `postgrestools.bin` configuration in your `.vscode/settings.json`. 24 | 2. You should not be prompted for any binary installations. 25 | 3. The extension should work when you open the `src/test.sql` file. 26 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-global-bin/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/simple-project-global-bin/src/test.sql: -------------------------------------------------------------------------------- 1 | select * from users; -------------------------------------------------------------------------------- /test/fixtures/vscode-settings-strat/.gitignore: -------------------------------------------------------------------------------- 1 | bin/* -------------------------------------------------------------------------------- /test/fixtures/vscode-settings-strat/.vscode/settings.json: -------------------------------------------------------------------------------- 1 | { 2 | "postgrestools.configFile": "./configs/postgrestools.jsonc", 3 | "postgrestools.bin": "./bin/postgrestools" 4 | // "postgrestools.bin": "/bin/postgrestools" 5 | } 6 | -------------------------------------------------------------------------------- /test/fixtures/vscode-settings-strat/README.md: -------------------------------------------------------------------------------- 1 | # VSCode Settings Strategy 2 | 3 | A bread-and-butter project with a `postgrestools.jsonc` on top level and SQL files in the `test` folder. 4 | The goal is to let the `.vscode/settings.json` point to a binary and have that config working correctly. 5 | 6 | ## Expectations 7 | 8 | The extension should recognize the `postgrestools.jsonc` file and connect with the right database. It should work when opening the `src/test.sql` file. 9 | 10 | ## Test protocol 11 | 12 | ### Via VSCode Settings 13 | 14 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 15 | 1. Download a matching [postgrestools binary](https://github.com/supabase-community/postgres_lsp/releases/latest) and move it into `./bin`. 16 | 2. Open the `.vscode/settings.json` and point to the binary, either via an absolute path or a relative path. 17 | 3. You should not be prompted for any binary installations. 18 | 4. The extension should work when you open the `src/test.sql` file. 19 | -------------------------------------------------------------------------------- /test/fixtures/vscode-settings-strat/configs/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/vscode-settings-strat/src/test.sql: -------------------------------------------------------------------------------- 1 | select * from users; -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/.gitignore: -------------------------------------------------------------------------------- 1 | .yarn 2 | !.yarn/patches 3 | !.yarn/plugins 4 | !.yarn/releases 5 | !.yarn/sdks 6 | !.yarn/versions 7 | 8 | # Swap the comments on the following lines if you wish to use zero-installs 9 | # In that case, don't forget to run `yarn config set enableGlobalCache false`! 10 | # Documentation here: https://yarnpkg.com/features/caching#zero-installs 11 | 12 | #!.yarn/cache 13 | -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/README.md: -------------------------------------------------------------------------------- 1 | # Simple Project 2 | 3 | A bread-and-butter project with a `postgrestools.jsonc` on top level and SQL files in the `test` folder. The extension is installed via Yarn Plug'n'Play. 4 | 5 | ## Expectations 6 | 7 | The extension should recognize the `postgrestools.jsonc` file and connect with the right database. We shouldn't have to install a global binary. 8 | 9 | ## Test protocol 10 | 11 | 0. Follow the instructions in `GLOBAL_SETUP.md`. 12 | 1. Make sure you have `yarn` installed. 13 | 2. Run `yarn --version` to make sure you're using the yarn version specified in the `package.json#packageManager`. 14 | 3. Run `yarn install`. 15 | 4. Make sure you do not have a folder containing a `postgrestools(.exe)` binary in your $PATH. 16 | 5. You should not be prompted for any binary installations. 17 | 6. You should be able to open the `src/test.sql` file and the extension should work. 18 | -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/package.json: -------------------------------------------------------------------------------- 1 | { 2 | "name": "my-project", 3 | "version": "0.0.0", 4 | "packageManager": "yarn@4.5.0", 5 | "dependencies": { 6 | "@postgrestools/postgrestools": "latest" 7 | } 8 | } 9 | -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/postgrestools.jsonc: -------------------------------------------------------------------------------- 1 | { 2 | "vcs": { 3 | "enabled": false, 4 | "clientKind": "git", 5 | "useIgnoreFile": false 6 | }, 7 | "files": { 8 | "ignore": [] 9 | }, 10 | "linter": { 11 | "enabled": true, 12 | "rules": { 13 | "recommended": true 14 | } 15 | }, 16 | "db": { 17 | "host": "127.0.0.1", 18 | "port": 3333, 19 | "username": "postgres", 20 | "password": "example", 21 | "database": "postgres", 22 | "connTimeoutSecs": 10 23 | } 24 | } 25 | -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/src/test.sql: -------------------------------------------------------------------------------- 1 | select email from users; -------------------------------------------------------------------------------- /test/fixtures/yarn-pnp-strat/yarn.lock: -------------------------------------------------------------------------------- 1 | # THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. 2 | # yarn lockfile v1 3 | 4 | 5 | "@postgrestools/cli-aarch64-apple-darwin@0.4.0": 6 | version "0.4.0" 7 | resolved "https://registry.npmjs.org/@postgrestools/cli-aarch64-apple-darwin/-/cli-aarch64-apple-darwin-0.4.0.tgz" 8 | integrity sha512-L+kitPI2tTIdJiA0bFRg2h1Eyhd5OHbs7cyDrDXfZMq49CLE8iAEWln9W1u45Uk2kLCyRCaqMn7WYfV0pVCciw== 9 | 10 | "@postgrestools/postgrestools@latest": 11 | version "0.4.0" 12 | resolved "https://registry.npmjs.org/@postgrestools/postgrestools/-/postgrestools-0.4.0.tgz" 13 | integrity sha512-is6UlTgtAVr42ZRXdM4QmvhPvQgweVaUk0ETzOqR4hjpARxwX6F6iiRcuW5+gHnt1cXocFLKO0djHGcErOywBw== 14 | optionalDependencies: 15 | "@postgrestools/cli-aarch64-apple-darwin" "0.4.0" 16 | "@postgrestools/cli-aarch64-linux-gnu" "0.4.0" 17 | "@postgrestools/cli-aarch64-windows-msvc" "0.4.0" 18 | "@postgrestools/cli-x86_64-apple-darwin" "0.4.0" 19 | "@postgrestools/cli-x86_64-linux-gnu" "0.4.0" 20 | "@postgrestools/cli-x86_64-windows-msvc" "0.4.0" 21 | -------------------------------------------------------------------------------- /tsconfig.json: -------------------------------------------------------------------------------- 1 | { 2 | "compilerOptions": { 3 | "module": "Node16", 4 | "target": "ES2022", 5 | "outDir": "out", 6 | "lib": ["ES2022"], 7 | "resolveJsonModule": true, 8 | "sourceMap": true, 9 | "rootDir": "src", 10 | "strict": true /* enable all strict type-checking options */ 11 | /* Additional Checks */ 12 | // "noImplicitReturns": true, /* Report error when not all code paths in function return a value. */ 13 | // "noFallthroughCasesInSwitch": true, /* Report errors for fallthrough cases in switch statement. */ 14 | // "noUnusedParameters": true, /* Report errors on unused parameters. */ 15 | } 16 | } 17 | --------------------------------------------------------------------------------