From fe6fd8be691c5ac0019628429bdd7b3c5bd24420 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tom=C3=A1=C5=A1=20=C4=8Cern=C3=ADk?= Date: Mon, 26 Oct 2026 08:53:12 +0100 Subject: [PATCH] VS Code CMake Helper: first implementation CMake the way CLion does it (profiles and presets, targets, run and debug configurations, CTest), moved out of personal-assistant's cmake-extension and renamed from CMake Targets. - Gitea Actions: typecheck, package the .vsix, release it on a v* tag - Updates: the latest Gitea release is looked at every 10 minutes and offered for install; a private repository's token in the secret storage - cmakeHelper.toolbar: the toolbar in the status bar, the editors' title, or VS Code's title bar (workbench.html patched, offered again after VS Code or extension updates) Co-Authored-By: Claude Opus 5.5 --- .gitea/workflows/build.yml | 53 + .gitignore | 3 + .vscode/launch.json | 13 + .vscodeignore | 6 + README.md | 81 + media/cmake.svg | 1 + media/titlebar.js | 149 ++ package-lock.json | 3606 ++++++++++++++++++++++++++++++++++++ package.json | 1022 ++++++++++ src/codeLens.ts | 62 + src/editorPage.ts | 94 + src/extension.ts | 357 ++++ src/fileApi.ts | 185 ++ src/gdbConfigs.ts | 431 +++++ src/gdbEditor.ts | 274 +++ src/launcher.ts | 192 ++ src/presets.ts | 172 ++ src/profileEditor.ts | 354 ++++ src/profiles.ts | 133 ++ src/project.ts | 468 +++++ src/runConfigEditor.ts | 211 +++ src/statusBar.ts | 114 ++ src/tests.ts | 386 ++++ src/titleBar.ts | 158 ++ src/updates.ts | 180 ++ src/util.ts | 181 ++ src/views.ts | 158 ++ tsconfig.json | 17 + 28 files changed, 9061 insertions(+) create mode 100644 .gitea/workflows/build.yml create mode 100644 .gitignore create mode 100644 .vscode/launch.json create mode 100644 .vscodeignore create mode 100644 README.md create mode 100644 media/cmake.svg create mode 100644 media/titlebar.js create mode 100644 package-lock.json create mode 100644 package.json create mode 100644 src/codeLens.ts create mode 100644 src/editorPage.ts create mode 100644 src/extension.ts create mode 100644 src/fileApi.ts create mode 100644 src/gdbConfigs.ts create mode 100644 src/gdbEditor.ts create mode 100644 src/launcher.ts create mode 100644 src/presets.ts create mode 100644 src/profileEditor.ts create mode 100644 src/profiles.ts create mode 100644 src/project.ts create mode 100644 src/runConfigEditor.ts create mode 100644 src/statusBar.ts create mode 100644 src/tests.ts create mode 100644 src/titleBar.ts create mode 100644 src/updates.ts create mode 100644 src/util.ts create mode 100644 src/views.ts create mode 100644 tsconfig.json diff --git a/.gitea/workflows/build.yml b/.gitea/workflows/build.yml new file mode 100644 index 0000000..ad6fdb3 --- /dev/null +++ b/.gitea/workflows/build.yml @@ -0,0 +1,53 @@ +name: Build + +on: + push: + branches: [main] + tags: ["v*"] + pull_request: + branches: [main] + workflow_dispatch: + +jobs: + build: + runs-on: ubuntu-latest + steps: + - name: Checkout + uses: actions/checkout@v4 + + - name: Setup Node.js + uses: actions/setup-node@v4 + with: + node-version: 22 + cache: npm + + - name: Install dependencies + run: npm ci + + - name: Typecheck + run: npm run typecheck + + - name: Check tag matches package.json version + if: startsWith(github.ref, 'refs/tags/v') + run: | + pkg="v$(node -p 'require("./package.json").version')" + if [ "$pkg" != "${GITHUB_REF_NAME}" ]; then + echo "Tag ${GITHUB_REF_NAME} does not match package.json version $pkg" + exit 1 + fi + + - name: Package + run: npm run package -- --skip-license + + # upload-artifact@v4 is not supported by older Gitea versions + - name: Upload artifact + uses: actions/upload-artifact@v3 + with: + name: vsix + path: "*.vsix" + + - name: Release + if: startsWith(github.ref, 'refs/tags/v') + uses: akkuman/gitea-release-action@v1 + with: + files: "*.vsix" diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..d3e15b1 --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +node_modules/ +out/ +*.vsix diff --git a/.vscode/launch.json b/.vscode/launch.json new file mode 100644 index 0000000..a142310 --- /dev/null +++ b/.vscode/launch.json @@ -0,0 +1,13 @@ +{ + "version": "0.2.0", + "configurations": [ + { + "name": "Run Extension", + "type": "extensionHost", + "request": "launch", + "args": ["--extensionDevelopmentPath=${workspaceFolder}"], + "outFiles": ["${workspaceFolder}/out/**/*.js"], + "preLaunchTask": "npm: compile" + } + ] +} diff --git a/.vscodeignore b/.vscodeignore new file mode 100644 index 0000000..a47ca29 --- /dev/null +++ b/.vscodeignore @@ -0,0 +1,6 @@ +src/** +node_modules/** +tsconfig.json +**/*.map +.gitignore +.vscode/** diff --git a/README.md b/README.md new file mode 100644 index 0000000..76192d3 --- /dev/null +++ b/README.md @@ -0,0 +1,81 @@ +# VS Code CMake Helper + +CMake in VS Code the way CLion does it. Standalone: it doesn't need (or want) Microsoft's CMake Tools, so turn that off in workspaces where this one runs. Projects are read through CMake's File API (CMake 3.14+; presets need 3.19+, test results 3.21+). + +## What it does + +- **Profiles** (the Profiles view, the `$(layers)` item in the status bar): each one is a build directory with its own build type, generator, toolchain, cache variables and environment. They come from the `cmakeHelper.profiles` setting and from the configure presets in `CMakePresets.json` / `CMakeUserPresets.json`. With neither, there's CLion's default: **Debug** in `cmake-build-debug`. Switching profiles reads that directory's last configure if there is one, and configures otherwise. +- **Targets** (the Targets view): executables, libraries and custom targets (`add_custom_target`; `showUtilityTargets` turns them off; CTest's dashboard targets are never listed), each with its sources in CMake's source groups. Inline buttons ▶ Run, 🐞 Debug, 🔨 Build. The menu has *Set as Active*, *Edit Run Configuration…*, *Rebuild*, *Go to Definition in CMakeLists* and *Copy Path of the Built File*. +- **One active target** in the status bar: `$(target) app`, then 🔨 ▶ 🐞. Keys as in CLion: **Ctrl+F9** build, **Shift+F10** run, **Shift+F9** debug, **Alt+Shift+F10** choose the target. +- **Run configurations per target**, edited as CLion's *Run/Debug Configurations* (*Edit Run Configuration…* on a target, or *Edit Configurations…* in the target picker): every target on the left, by kind; on the right its executable (the target's own, another target's, or a file: what a custom target or a library runs after it's built), program arguments, working directory (default: the executable's directory), environment (`A=1;B=2`) and *Before launch: Build*. A custom target with no executable is only built by ▶, as in CLion. They're kept in the workspace settings under `cmakeHelper.runConfigurations`. +- **Debugging** through C/C++ (`cppdbg`, or `cppvsdbg` with MSVC), CodeLLDB or LLDB DAP: the first one installed, or the one set in `cmakeHelper.debugger`. Extra launch properties go in `cmakeHelper.debugConfiguration`. +- **CTest in the Test Explorer**: tests are grouped by the target whose executable they run, and by suite for names like `Suite.Test`. Each one sits at its `TEST(…)` / `TEST_CASE("…")` / `BOOST_AUTO_TEST_CASE(…)` in the sources (with ▶ in the gutter), or else at its `add_test`. Running builds the tests' targets first, then calls `ctest` and reads the results from JUnit. Debugging starts the test's command under the debugger. Tests that `gtest_discover_tests` finds only after a build show up once the build is done. +- **Code lenses**: ▶ Run / 🐞 Debug above `main()` in an executable's sources, and Run / Debug / Build above each `add_executable` / `add_library` in CMake files. +- **Load CMake Project** on a `CMakeLists.txt` (the explorer's and the editor's context menu, as in CLion): that folder becomes the project (`cmakeHelper.sourceDirectory`) and it's configured; on the project's own `CMakeLists.txt` the menu has *Reload CMake Project*. +- **Reload**: when one of the project's CMake files changes, a notification and a `Reload CMake` item in the status bar appear (`cmakeHelper.autoReload`: `ask`, `always`, `never`). *Reset Cache and Reload Project* removes `CMakeCache.txt` and `CMakeFiles/` first. A generator mismatch offers the reset by itself. +- `compile_commands.json` is exported (`exportCompileCommands`), and can be copied where clangd looks for it (`copyCompileCommands`, e.g. `${sourceDir}/compile_commands.json`). +- For `launch.json` / `tasks.json`: `${command:cmakeHelper.activeProgram}`, `${command:cmakeHelper.activeTarget}`, `${command:cmakeHelper.buildDirectory}`, `${command:cmakeHelper.buildType}`. + +## Embedded and remote debugging + +As CLion's *Embedded GDB Server* and *Remote GDB Server* run configurations: **CMake: Edit GDB Server Configurations…** (or *New GDB Server Configuration…* in the target picker). A configuration chosen in the target picker (it shows there under the targets) is what the toolbar's 🔨, ▶ and 🐞 act on; choosing a target goes back to running it here. + +- **Embedded GDB Server**: a GDB server started here (OpenOCD, SEGGER J-Link, ST-LINK's st-util, pyOCD: *Fill in for* sets the usual arguments), gdb (`gdb-multiarch`, `arm-none-eabi-gdb`…) connected with `target remote`, the program downloaded (*Always*, *If updated*, *Never*) between two reset commands. 🐞 debugs it; ▶ is *Download & Run*: flash, start it (`monitor reset run` by default), detach, no debugger. +- **Remote GDB Server**: gdb connected to a gdbserver elsewhere, with a sysroot and path mappings. With an SSH host the program is first uploaded (scp) and gdbserver started there over SSH, and ▶ runs it there. + +Debugging goes through C/C++ (`ms-vscode.cpptools`, `cppdbg`), which starts the server and connects. The configurations are kept in the workspace setting `cmakeHelper.gdbConfigurations`. + +## Profiles in settings + +**CMake: Edit Profiles…** (the ⚙ on a profile, or in the profile picker) edits them as CLion's Settings › Build › CMake does: the list with add, remove, copy and move, and each profile's name, *Enable profile*, build type, toolchain file, generator, CMake options, build directory, build options and environment; OK / Cancel / Apply. Presets are shown read-only; copying one makes a profile of it. It writes the workspace setting below (cache variables become `-D` CMake options). + +```jsonc +"cmakeHelper.profiles": [ + { "name": "Debug", "buildType": "Debug" }, + { "name": "Release", "buildType": "Release", "generator": "Ninja" }, + { + "name": "ASan", + "buildType": "RelWithDebInfo", + "buildDirectory": "cmake-build-asan", + "cacheVariables": { "CMAKE_CXX_FLAGS": "-fsanitize=address", "BUILD_TESTING": true }, + "environment": { "ASAN_OPTIONS": "detect_leaks=1" } + } +] +``` + +## Toolbar + +`cmakeHelper.toolbar` says where CLion's toolbar (profile, target, Build, Run, +Debug) goes: + +- `statusBar` (default): in the status bar. +- `editorTitle`: Build, Run, Debug and the profile and target pickers as + buttons at the top of the editors; the names stay in the status bar. +- `titleBar`: in VS Code's title bar, as in Jarvis's web IDE. An extension + can't put anything there, so VS Code itself is patched: `media/titlebar.js` + goes next to its `workbench.html` and into it, and `product.json`'s checksum + is updated so VS Code doesn't call its installation corrupt. VS Code is + usually root's, so it asks for your password (pkexec). After a VS Code update + (which drops the patch) or an update of this extension, it offers to patch + again. **CMake: Remove the CMake Toolbar from the Title Bar** undoes it. + +## Updates + +The extension looks at the latest release of `shin/VS-Code-CMake-helper` on +git.cernikovi.cz on start and every 10 minutes, and offers a newer one: Install +downloads its `.vsix` and installs it. The repository is private, so it needs a +Gitea access token with read access: **CMake: Set Gitea Token for CMake Helper +Updates** (kept in VS Code's secret storage). **CMake: Check for CMake Helper +Updates** looks now; `cmakeHelper.updates.check` turns it off. + +A release is made by the CI on a `v*` tag matching the version in `package.json`. + +## Building + +``` +npm install +npm run compile # out/ +npm run package # .vsix +``` + +To try it, open this folder in VS Code and press F5 (Run Extension), or install the `.vsix`. diff --git a/media/cmake.svg b/media/cmake.svg new file mode 100644 index 0000000..88716f4 --- /dev/null +++ b/media/cmake.svg @@ -0,0 +1 @@ + diff --git a/media/titlebar.js b/media/titlebar.js new file mode 100644 index 0000000..3775af4 --- /dev/null +++ b/media/titlebar.js @@ -0,0 +1,149 @@ +/* + * CLion's toolbar in VS Code's title bar: the CMake profile and target, then + * Build, Run and Debug, next to the command center. An extension can't put + * anything in the title bar, so CMake Helper copies this script next to VS + * Code's workbench.html and adds it there (src/titleBar.ts). It draws nothing + * of its own: it mirrors the extension's status bar items (text, icon, + * tooltip, colours) and a click on one clicks the item, so the extension + * keeps all the logic. While it's shown, those status bar items are hidden. + * Plain DOM, no innerHTML: the workbench requires trusted types. + */ +(() => { + const PREFIX = "cernik.vs-code-cmake-helper.cmakeHelper."; + /** The mirrored items, in the toolbar's order; `group` items share one bordered box, like CLion's run configuration. */ + const ITEMS = [ + { id: "reload", kind: "text" }, + { id: "profile", kind: "text", group: true }, + { id: "target", kind: "text", group: true }, + { id: "build", kind: "icon" }, + { id: "run", kind: "icon", color: "run" }, + { id: "debug", kind: "icon", color: "run" }, + ]; + + const css = ` +body.cmake-helper-toolbar ${ITEMS.map((i) => `#${CSS.escape(PREFIX + i.id)}`).join(", ")} { display: none !important; } +/* Above the title bar's drag region, as VS Code's own toolbars are. */ +.cmake-helper-toolbar-bar { position: relative; z-index: 2500; display: flex; align-items: center; gap: 2px; height: 100%; padding: 0 6px; -webkit-app-region: no-drag; app-region: no-drag; } +.cmake-helper-toolbar-bar button { display: flex; align-items: center; gap: 4px; height: 22px; padding: 0 5px; border: 0; border-radius: 5px; background: none; + color: inherit; font: inherit; font-size: 12px; cursor: pointer; white-space: nowrap; max-width: 220px; } +.cmake-helper-toolbar-bar button:hover { background: var(--vscode-toolbar-hoverBackground); } +.cmake-helper-toolbar-bar button .label { overflow: hidden; text-overflow: ellipsis; } +.cmake-helper-toolbar-bar button.icon { width: 24px; justify-content: center; padding: 0; } +.cmake-helper-toolbar-bar button.run .codicon { color: var(--vscode-debugIcon-startForeground, #89d185); } +.cmake-helper-toolbar-bar button .chevron { opacity: .7; font-size: 12px; } +.cmake-helper-toolbar-bar .group { display: flex; align-items: center; height: 22px; margin-right: 4px; border: 1px solid var(--vscode-commandCenter-border, rgba(128,128,128,.35)); border-radius: 6px; } +.cmake-helper-toolbar-bar .group button { border-radius: 0; height: 20px; } +.cmake-helper-toolbar-bar .group button:first-child { border-radius: 5px 0 0 5px; } +.cmake-helper-toolbar-bar .group button:last-child { border-radius: 0 5px 5px 0; } +.cmake-helper-toolbar-bar .group button + button { border-left: 1px solid var(--vscode-commandCenter-border, rgba(128,128,128,.35)); } +`; + + /** The status bar item's own clickable label, if the extension shows it now. */ + const original = (id) => document.getElementById(PREFIX + id)?.querySelector("a.statusbar-item-label"); + + /** "play, Run test (Shift+F10)": the tooltip part of VS Code's aria-label (the part before is the icon's name). */ + const tooltipOf = (a) => { + const label = a.getAttribute("aria-label") ?? ""; + const i = label.indexOf(", "); + return i >= 0 ? label.slice(i + 2) : label; + }; + + function button(item, a) { + const b = document.createElement("button"); + b.type = "button"; + b.className = [item.kind, item.color ?? ""].join(" ").trim(); + b.title = tooltipOf(a); + b.setAttribute("aria-label", b.title); + // The icon (a codicon span, VS Code's own font) and the text, as the item shows them. + const icon = a.querySelector(".codicon")?.cloneNode(true); + if (icon) b.append(icon); + const text = a.textContent.trim(); + if (item.kind === "text" && text) { + const label = document.createElement("span"); + label.className = "label"; + label.textContent = text; + b.append(label); + if (item.group) { + const chevron = document.createElement("span"); + chevron.className = "codicon codicon-chevron-down chevron"; + b.append(chevron); + } + } + // A warning-coloured item (Reload CMake) keeps its colours. + const box = a.parentElement; + if (box?.style.backgroundColor) b.style.backgroundColor = box.style.backgroundColor; + if (box?.style.color || a.style.color) b.style.color = box.style.color || a.style.color; + b.addEventListener("click", (e) => { + e.preventDefault(); + original(item.id)?.click(); + }); + return b; + } + + let bar = null; + let last = ""; + + function render() { + const right = document.querySelector(".part.titlebar .titlebar-right"); + const present = ITEMS.map((item) => ({ item, a: original(item.id) })).filter((x) => x.a); + // No project (or no title bar): the status bar shows whatever there is. + if (!right || !present.length) { + bar?.remove(); + bar = null; + last = ""; + document.body.classList.remove("cmake-helper-toolbar"); + return; + } + // Rebuild only when something shown changed. + const key = present.map(({ item, a }) => `${item.id}\u0000${a.outerHTML}\u0000${a.parentElement?.getAttribute("style") ?? ""}`).join("\u0001"); + if (bar && bar.isConnected && key === last) return; + last = key; + if (!bar || !bar.isConnected) { + bar = document.createElement("div"); + bar.className = "cmake-helper-toolbar-bar"; + right.prepend(bar); + } + bar.replaceChildren(); + let group = null; + for (const { item, a } of present) { + const b = button(item, a); + if (item.group) { + if (!group) { + group = document.createElement("div"); + group.className = "group"; + bar.append(group); + } + group.append(b); + } else { + group = null; + bar.append(b); + } + } + document.body.classList.add("cmake-helper-toolbar"); + } + + let queued = false; + const schedule = () => { + if (queued) return; + queued = true; + requestAnimationFrame(() => { + queued = false; + render(); + }); + }; + + function start() { + const style = document.createElement("style"); + style.textContent = css; + document.head.append(style); + // The status bar changes (items come, go and change text) and the title bar may be redrawn: follow both. + new MutationObserver((records) => { + if (bar && records.every((r) => bar.contains(r.target))) return; + schedule(); + }).observe(document.body, { subtree: true, childList: true, characterData: true, attributes: true, attributeFilter: ["aria-label", "style", "class"] }); + schedule(); + } + + if (document.body) start(); + else document.addEventListener("DOMContentLoaded", start); +})(); diff --git a/package-lock.json b/package-lock.json new file mode 100644 index 0000000..4a571f3 --- /dev/null +++ b/package-lock.json @@ -0,0 +1,3606 @@ +{ + "name": "vs-code-cmake-helper", + "version": "0.1.0", + "lockfileVersion": 3, + "requires": true, + "packages": { + "": { + "name": "vs-code-cmake-helper", + "version": "0.1.0", + "license": "UNLICENSED", + "devDependencies": { + "@types/node": "^20.0.0", + "@types/vscode": "~1.85.0", + "@vscode/vsce": "^3.9.2", + "typescript": "^5.6.0" + }, + "engines": { + "vscode": "^1.85.0" + } + }, + "node_modules/@azu/format-text": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/@azu/format-text/-/format-text-1.0.2.tgz", + "integrity": "sha512-Swi4N7Edy1Eqq82GxgEECXSSLyn6GOb5htRFPzBDdUkECGXtlf12ynO5oJSpWKPwCaUssOu7NfhDcCWpIC6Ywg==", + "dev": true + }, + "node_modules/@azu/style-format": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@azu/style-format/-/style-format-1.0.1.tgz", + "integrity": "sha512-AHcTojlNBdD/3/KxIKlg8sxIWHfOtQszLvOpagLTO+bjC3u7SAszu1lf//u7JJC50aUSH+BVWDD/KvaA6Gfn5g==", + "dev": true, + "dependencies": { + "@azu/format-text": "^1.0.1" + } + }, + "node_modules/@azure/abort-controller": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.2.0.tgz", + "integrity": "sha512-fNAjWnA/nZ2jz31kxR/AqRaUT8ewHBw/WuBIosK0moMy1C9e5ValbDfFdIxJzVOOYaYkV/b2F1S4H/aHiqfVQg==", + "dev": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-auth": { + "version": "1.11.0", + "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.11.0.tgz", + "integrity": "sha512-IUZydyTUkDnYdstOW9pFOOUQlBjAepK5teihDE3x6yxsPJs/hsAaaYpeGxdxrgtOiJbBKSjKW7MDk7AEhb4LRg==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-util": "^1.13.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-client": { + "version": "1.11.1", + "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.11.1.tgz", + "integrity": "sha512-2QygG2F76ZpMP2eMztiJvAiFMu71M9rDeU7vO/QKg5Css7MgM4frUOslFjhVjRhbGaCNPtz/S8M6y46/fFKVuQ==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-rest-pipeline": "^1.22.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-process": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/@azure/core-process/-/core-process-1.0.0.tgz", + "integrity": "sha512-/shnJ+ooO8WPxDhPEeI/2oRQuubn16gZ6CvlbpWbEswZfzwI9tI/sMAHmF3x1LuQ9yZYXfLW3TjzGMLEC5blKg==", + "dev": true, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-rest-pipeline": { + "version": "1.25.0", + "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.25.0.tgz", + "integrity": "sha512-bMs8ekJLjX8wPV+9IPBges1SLPyuDtE9g5gLDWOpxzKcoOFQnpLGkbcT1tdw3FaAmDS1gnPmMmJ6y/T5B96kIA==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@azure/core-auth": "^1.10.0", + "@azure/core-tracing": "^1.3.0", + "@azure/core-util": "^1.13.0", + "@azure/logger": "^1.3.0", + "@typespec/ts-http-runtime": "^0.3.4", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-tracing": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.4.0.tgz", + "integrity": "sha512-eGwxD0AtncrxeBM4tG8R55Pc3rdX1hNW2WibJAgYpCVA6E93mvvVH+LcssoVjOBrSKWS55yEIHsk0X8ctHmfOQ==", + "dev": true, + "dependencies": { + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/core-util": { + "version": "1.14.0", + "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.14.0.tgz", + "integrity": "sha512-9n2pWK61veAuN0V20t9lOuoV4CFMdyAZ1ygZzvBGk/pBBJRib/PjL9PLXa/aI2CcPpyHfqVsxxqLCYl6uZlfDw==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^2.1.2", + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/identity": { + "version": "4.14.0", + "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.14.0.tgz", + "integrity": "sha512-8utCLdTZn14CiAe0HW4JbtzUMsc0mdDwnjUvStFMrR4IxQP8S2eL9zVAdplpfORVJGYmidZElpeXRYjJQ4UyUw==", + "dev": true, + "dependencies": { + "@azure/abort-controller": "^2.0.0", + "@azure/core-auth": "^1.9.0", + "@azure/core-client": "^1.9.2", + "@azure/core-process": "^1.0.0", + "@azure/core-rest-pipeline": "^1.17.0", + "@azure/core-tracing": "^1.0.0", + "@azure/core-util": "^1.11.0", + "@azure/logger": "^1.0.0", + "@azure/msal-browser": "^5.5.0", + "@azure/msal-node": "^6.0.0", + "open": "^11.0.0", + "tslib": "^2.2.0" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/logger": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.4.0.tgz", + "integrity": "sha512-rbAE25KUfjU/s3XHUdJgceoCP5dEOpMx85J04kF+QMdta73XkuG9JGHHinch+XIoKpBdqljin+KqURpJriSzLA==", + "dev": true, + "dependencies": { + "@typespec/ts-http-runtime": "^0.3.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@azure/msal-browser": { + "version": "5.26.0", + "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-5.26.0.tgz", + "integrity": "sha512-GYKRquzDZmZggCdStWy2bNJKnPui1upssSuznJgP+/h6Mb0ldkUkaGvaY6uSGGmZOm0CGiBQ3b5BiGy43BpXSg==", + "dev": true, + "dependencies": { + "@azure/msal-common": "16.14.3" + }, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-common": { + "version": "16.14.3", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.3.tgz", + "integrity": "sha512-tz3GvMrR9V/plb2lDdW8qaO8CPpfZevdExBKjltzzbAtyhK0eB2UIvji1aU+gkyHj3fxQ2ie+LNHyMXdmo8wOg==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@azure/msal-node": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-6.0.1.tgz", + "integrity": "sha512-ixSO1Y/kCVRthRs+hSx/5qkwaunX1/RAePhlMN0wIpIQ4WEZ6AREGGnGd1AP0qspHVsAwzWQKGubpjh50JyJIQ==", + "dev": true, + "dependencies": { + "@azure/msal-common": "16.14.1", + "jsonwebtoken": "^9.0.0" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/@azure/msal-node/node_modules/@azure/msal-common": { + "version": "16.14.1", + "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-16.14.1.tgz", + "integrity": "sha512-Or6xhPNyi4zHW25158yxBoyxuCqNSPa5YBVqfF1J5Ks4MJWBo/USXdp05DQIPu1Zli00YZu6t0+h6KvHJealxQ==", + "dev": true, + "engines": { + "node": ">=0.8.0" + } + }, + "node_modules/@babel/code-frame": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.29.7.tgz", + "integrity": "sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==", + "dev": true, + "dependencies": { + "@babel/helper-validator-identifier": "^7.29.7", + "js-tokens": "^4.0.0", + "picocolors": "^1.1.1" + }, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@babel/helper-validator-identifier": { + "version": "7.29.7", + "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.29.7.tgz", + "integrity": "sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==", + "dev": true, + "engines": { + "node": ">=6.9.0" + } + }, + "node_modules/@nodelib/fs.scandir": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", + "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "2.0.5", + "run-parallel": "^1.1.9" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.stat": { + "version": "2.0.5", + "resolved": "https://registry.npmjs.org/@nodelib/fs.stat/-/fs.stat-2.0.5.tgz", + "integrity": "sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@nodelib/fs.walk": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/@nodelib/fs.walk/-/fs.walk-1.2.8.tgz", + "integrity": "sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==", + "dev": true, + "dependencies": { + "@nodelib/fs.scandir": "2.1.5", + "fastq": "^1.6.0" + }, + "engines": { + "node": ">= 8" + } + }, + "node_modules/@secretlint/config-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-creator/-/config-creator-10.2.2.tgz", + "integrity": "sha512-BynOBe7Hn3LJjb3CqCHZjeNB09s/vgf0baBaHVw67w7gHF0d25c3ZsZ5+vv8TgwSchRdUCRrbbcq5i2B1fJ2QQ==", + "dev": true, + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/config-loader": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/config-loader/-/config-loader-10.2.2.tgz", + "integrity": "sha512-ndjjQNgLg4DIcMJp4iaRD6xb9ijWQZVbd9694Ol2IszBIbGPPkwZHzJYKICbTBmh6AH/pLr0CiCaWdGJU7RbpQ==", + "dev": true, + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "ajv": "^8.17.1", + "debug": "^4.4.1", + "rc-config-loader": "^4.1.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/core": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/core/-/core-10.2.2.tgz", + "integrity": "sha512-6rdwBwLP9+TO3rRjMVW1tX+lQeo5gBbxl1I5F8nh8bgGtKwdlCMhMKsBWzWg1ostxx/tIG7OjZI0/BxsP8bUgw==", + "dev": true, + "dependencies": { + "@secretlint/profiler": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "structured-source": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/formatter/-/formatter-10.2.2.tgz", + "integrity": "sha512-10f/eKV+8YdGKNQmoDUD1QnYL7TzhI2kzyx95vsJKbEa8akzLAR5ZrWIZ3LbcMmBLzxlSQMMccRmi05yDQ5YDA==", + "dev": true, + "dependencies": { + "@secretlint/resolver": "^10.2.2", + "@secretlint/types": "^10.2.2", + "@textlint/linter-formatter": "^15.2.0", + "@textlint/module-interop": "^15.2.0", + "@textlint/types": "^15.2.0", + "chalk": "^5.4.1", + "debug": "^4.4.1", + "pluralize": "^8.0.0", + "strip-ansi": "^7.1.0", + "table": "^6.9.0", + "terminal-link": "^4.0.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/formatter/node_modules/chalk": { + "version": "5.6.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-5.6.2.tgz", + "integrity": "sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==", + "dev": true, + "engines": { + "node": "^12.17.0 || ^14.13 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/@secretlint/node": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/node/-/node-10.2.2.tgz", + "integrity": "sha512-eZGJQgcg/3WRBwX1bRnss7RmHHK/YlP/l7zOQsrjexYt6l+JJa5YhUmHbuGXS94yW0++3YkEJp0kQGYhiw1DMQ==", + "dev": true, + "dependencies": { + "@secretlint/config-loader": "^10.2.2", + "@secretlint/core": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "@secretlint/source-creator": "^10.2.2", + "@secretlint/types": "^10.2.2", + "debug": "^4.4.1", + "p-map": "^7.0.3" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/profiler": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/profiler/-/profiler-10.2.2.tgz", + "integrity": "sha512-qm9rWfkh/o8OvzMIfY8a5bCmgIniSpltbVlUVl983zDG1bUuQNd1/5lUEeWx5o/WJ99bXxS7yNI4/KIXfHexig==", + "dev": true + }, + "node_modules/@secretlint/resolver": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/resolver/-/resolver-10.2.2.tgz", + "integrity": "sha512-3md0cp12e+Ae5V+crPQYGd6aaO7ahw95s28OlULGyclyyUtf861UoRGS2prnUrKh7MZb23kdDOyGCYb9br5e4w==", + "dev": true + }, + "node_modules/@secretlint/secretlint-formatter-sarif": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-formatter-sarif/-/secretlint-formatter-sarif-10.2.2.tgz", + "integrity": "sha512-ojiF9TGRKJJw308DnYBucHxkpNovDNu1XvPh7IfUp0A12gzTtxuWDqdpuVezL7/IP8Ua7mp5/VkDMN9OLp1doQ==", + "dev": true, + "dependencies": { + "node-sarif-builder": "^3.2.0" + } + }, + "node_modules/@secretlint/secretlint-rule-no-dotenv": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-no-dotenv/-/secretlint-rule-no-dotenv-10.2.2.tgz", + "integrity": "sha512-KJRbIShA9DVc5Va3yArtJ6QDzGjg3PRa1uYp9As4RsyKtKSSZjI64jVca57FZ8gbuk4em0/0Jq+uy6485wxIdg==", + "dev": true, + "dependencies": { + "@secretlint/types": "^10.2.2" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/secretlint-rule-preset-recommend": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/secretlint-rule-preset-recommend/-/secretlint-rule-preset-recommend-10.2.2.tgz", + "integrity": "sha512-K3jPqjva8bQndDKJqctnGfwuAxU2n9XNCPtbXVI5JvC7FnQiNg/yWlQPbMUlBXtBoBGFYp08A94m6fvtc9v+zA==", + "dev": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/source-creator": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/source-creator/-/source-creator-10.2.2.tgz", + "integrity": "sha512-h6I87xJfwfUTgQ7irWq7UTdq/Bm1RuQ/fYhA3dtTIAop5BwSFmZyrchph4WcoEvbN460BWKmk4RYSvPElIIvxw==", + "dev": true, + "dependencies": { + "@secretlint/types": "^10.2.2", + "istextorbinary": "^9.5.0" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@secretlint/types": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/@secretlint/types/-/types-10.2.2.tgz", + "integrity": "sha512-Nqc90v4lWCXyakD6xNyNACBJNJ0tNCwj2WNk/7ivyacYHxiITVgmLUFXTBOeCdy79iz6HtN9Y31uw/jbLrdOAg==", + "dev": true, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/@sindresorhus/merge-streams": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/@sindresorhus/merge-streams/-/merge-streams-2.3.0.tgz", + "integrity": "sha512-LtoMMhxAlorcGhmFYI+LhPgbPZCkgP6ra1YL604EeF6U98pLlQ3iWIGMdWSC+vWmPBWBNgmDBAhnAobLROJmwg==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/@textlint/ast-node-types": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/ast-node-types/-/ast-node-types-15.8.0.tgz", + "integrity": "sha512-5CiH9COYmovWmExQgs7763DzX6Gy9zjkjJ7JxCC95wyTcjwQn/8poNF6fv3qzRlmx8CRRde8DHr9FcgAAiPzgw==", + "dev": true + }, + "node_modules/@textlint/linter-formatter": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/linter-formatter/-/linter-formatter-15.8.0.tgz", + "integrity": "sha512-+oU3A235NATv6Lzi4xa4kJ65PuNJlIxesaO4AvDhDWA9FWm7y4XKWaoQCW1esgaQQ6dwnUiFKArQ8TcJ86mC4w==", + "dev": true, + "dependencies": { + "@azu/format-text": "^1.0.2", + "@azu/style-format": "^1.0.1", + "@textlint/module-interop": "15.8.0", + "@textlint/resolver": "15.8.0", + "@textlint/types": "15.8.0", + "debug": "^4.4.3", + "js-yaml": "^4.3.0", + "lodash": "^4.18.1", + "pluralize": "^2.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1", + "table": "^6.9.0", + "text-table": "^0.2.0" + }, + "engines": { + "node": ">=20.18.0" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/linter-formatter/node_modules/pluralize": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-2.0.0.tgz", + "integrity": "sha512-TqNZzQCD4S42De9IfnnBvILN7HAW7riLqsCyp8lgjXeysyPlX5HhqKAcJHHHb9XskE4/a+7VGC9zzx8Ls0jOAw==", + "dev": true + }, + "node_modules/@textlint/linter-formatter/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/@textlint/module-interop": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/module-interop/-/module-interop-15.8.0.tgz", + "integrity": "sha512-rt+OR1WYGoLOY8HkA/aBPrqufF6yUUEsKEAh7XohTsT3lp9IyZFT6zOIbjul9P4FAzsmSPkcrYjVx3Bz/IUfkg==", + "dev": true + }, + "node_modules/@textlint/resolver": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/resolver/-/resolver-15.8.0.tgz", + "integrity": "sha512-E88tzfX3K8Jykk+38aJ9cy8RquD8ABVOPTO2rFEESq0wcg8x6/ypdAS8ZgR7OKiGqlRF0hkO/m5PbQwVfKM3VA==", + "dev": true + }, + "node_modules/@textlint/types": { + "version": "15.8.0", + "resolved": "https://registry.npmjs.org/@textlint/types/-/types-15.8.0.tgz", + "integrity": "sha512-Anhc6y5736YIsvqae0U6k0YmB2M/QVHkEeOv2aydAn/WIkdI69dCOiDbe3/+RagS3qstFTSFWJzNRA2lUjv19w==", + "dev": true, + "dependencies": { + "@textlint/ast-node-types": "15.8.0" + } + }, + "node_modules/@types/node": { + "version": "20.19.43", + "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.43.tgz", + "integrity": "sha512-6oYBAi5ikg4Pl+kGsoYtawUMBT2zZMCvPNF7pVLnHZfd1zf38DRiWn/gT01RYCdUqkv7Fhr+C9ot4/tb+2sVvA==", + "dev": true, + "dependencies": { + "undici-types": "~6.21.0" + } + }, + "node_modules/@types/normalize-package-data": { + "version": "2.4.4", + "resolved": "https://registry.npmjs.org/@types/normalize-package-data/-/normalize-package-data-2.4.4.tgz", + "integrity": "sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==", + "dev": true + }, + "node_modules/@types/sarif": { + "version": "2.1.7", + "resolved": "https://registry.npmjs.org/@types/sarif/-/sarif-2.1.7.tgz", + "integrity": "sha512-kRz0VEkJqWLf1LLVN4pT1cg1Z9wAuvI6L97V3m2f5B76Tg8d413ddvLBPTEHAZJlnn4XSvu0FkZtViCQGVyrXQ==", + "dev": true + }, + "node_modules/@types/vscode": { + "version": "1.85.0", + "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.85.0.tgz", + "integrity": "sha512-CF/RBon/GXwdfmnjZj0WTUMZN5H6YITOfBCP4iEZlOtVQXuzw6t7Le7+cR+7JzdMrnlm7Mfp49Oj2TuSXIWo3g==", + "dev": true + }, + "node_modules/@typespec/ts-http-runtime": { + "version": "0.3.9", + "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.9.tgz", + "integrity": "sha512-edSdeAqkdxBVzA1yL1LrLCml1YjyCVvPMtMqJpbF+6K609tHe8V6sQUzFQSGcYNhcuhOceZtjvN32+mpIth30A==", + "dev": true, + "dependencies": { + "http-proxy-agent": "^7.0.0", + "https-proxy-agent": "^7.0.0", + "tslib": "^2.6.2" + }, + "engines": { + "node": ">=22.0.0" + } + }, + "node_modules/@vscode/vsce": { + "version": "3.9.2", + "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-3.9.2.tgz", + "integrity": "sha512-XSxMosEEDO6vLxELAHVkwmhC0qe0ijZni2jB9Rcs8kQsW4lhTDQ/wMzmwFs/buotAWSnpmUp/dRWD2ufG3UYKA==", + "dev": true, + "dependencies": { + "@azure/identity": "^4.1.0", + "@secretlint/node": "^10.1.2", + "@secretlint/secretlint-formatter-sarif": "^10.1.2", + "@secretlint/secretlint-rule-no-dotenv": "^10.1.2", + "@secretlint/secretlint-rule-preset-recommend": "^10.1.2", + "@vscode/vsce-sign": "^2.0.0", + "azure-devops-node-api": "^12.5.0", + "chalk": "^4.1.2", + "cheerio": "^1.0.0-rc.9", + "cockatiel": "^3.1.2", + "commander": "^12.1.0", + "form-data": "^4.0.0", + "glob": "^13.0.6", + "hosted-git-info": "^4.0.2", + "jsonc-parser": "^3.2.0", + "leven": "^3.1.0", + "markdown-it": "^14.1.0", + "mime": "^1.3.4", + "minimatch": "^10.2.2", + "parse-semver": "^1.1.1", + "read": "^1.0.7", + "secretlint": "^10.1.2", + "semver": "^7.5.2", + "tmp": "^0.2.3", + "typed-rest-client": "^1.8.4", + "url-join": "^4.0.1", + "xml2js": "^0.5.0", + "yauzl": "^3.2.1", + "yazl": "^2.2.2" + }, + "bin": { + "vsce": "vsce" + }, + "engines": { + "node": ">= 20" + }, + "optionalDependencies": { + "keytar": "^7.7.0" + } + }, + "node_modules/@vscode/vsce-sign": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.1.0.tgz", + "integrity": "sha512-9AQrqazrBgTgRSuwleLVXUrIUphY02/SFCh2TKYoLV/xifJAdblhdmEmw5gUrYSPQ3sRwNs9iyCMD14sATEE6g==", + "dev": true, + "hasInstallScript": true, + "optionalDependencies": { + "@vscode/vsce-sign-alpine-arm64": "2.0.6", + "@vscode/vsce-sign-alpine-x64": "2.0.6", + "@vscode/vsce-sign-darwin-arm64": "2.0.6", + "@vscode/vsce-sign-darwin-x64": "2.0.6", + "@vscode/vsce-sign-linux-arm": "2.0.6", + "@vscode/vsce-sign-linux-arm64": "2.0.6", + "@vscode/vsce-sign-linux-x64": "2.0.6", + "@vscode/vsce-sign-win32-arm64": "2.0.6", + "@vscode/vsce-sign-win32-x64": "2.0.6" + } + }, + "node_modules/@vscode/vsce-sign-alpine-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-arm64/-/vsce-sign-alpine-arm64-2.0.6.tgz", + "integrity": "sha512-wKkJBsvKF+f0GfsUuGT0tSW0kZL87QggEiqNqK6/8hvqsXvpx8OsTEc3mnE1kejkh5r+qUyQ7PtF8jZYN0mo8Q==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-alpine-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-alpine-x64/-/vsce-sign-alpine-x64-2.0.6.tgz", + "integrity": "sha512-YoAGlmdK39vKi9jA18i4ufBbd95OqGJxRvF3n6ZbCyziwy3O+JgOpIUPxv5tjeO6gQfx29qBivQ8ZZTUF2Ba0w==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "alpine" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-arm64/-/vsce-sign-darwin-arm64-2.0.6.tgz", + "integrity": "sha512-5HMHaJRIQuozm/XQIiJiA0W9uhdblwwl2ZNDSSAeXGO9YhB9MH5C4KIHOmvyjUnKy4UCuiP43VKpIxW1VWP4tQ==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-darwin-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-darwin-x64/-/vsce-sign-darwin-x64-2.0.6.tgz", + "integrity": "sha512-25GsUbTAiNfHSuRItoQafXOIpxlYj+IXb4/qarrXu7kmbH94jlm5sdWSCKrrREs8+GsXF1b+l3OB7VJy5jsykw==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "darwin" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm/-/vsce-sign-linux-arm-2.0.6.tgz", + "integrity": "sha512-UndEc2Xlq4HsuMPnwu7420uqceXjs4yb5W8E2/UkaHBB9OWCwMd3/bRe/1eLe3D8kPpxzcaeTyXiK3RdzS/1CA==", + "cpu": [ + "arm" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-arm64/-/vsce-sign-linux-arm64-2.0.6.tgz", + "integrity": "sha512-cfb1qK7lygtMa4NUl2582nP7aliLYuDEVpAbXJMkDq1qE+olIw/es+C8j1LJwvcRq1I2yWGtSn3EkDp9Dq5FdA==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-linux-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-linux-x64/-/vsce-sign-linux-x64-2.0.6.tgz", + "integrity": "sha512-/olerl1A4sOqdP+hjvJ1sbQjKN07Y3DVnxO4gnbn/ahtQvFrdhUi0G1VsZXDNjfqmXw57DmPi5ASnj/8PGZhAA==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "linux" + ] + }, + "node_modules/@vscode/vsce-sign-win32-arm64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-arm64/-/vsce-sign-win32-arm64-2.0.6.tgz", + "integrity": "sha512-ivM/MiGIY0PJNZBoGtlRBM/xDpwbdlCWomUWuLmIxbi1Cxe/1nooYrEQoaHD8ojVRgzdQEUzMsRbyF5cJJgYOg==", + "cpu": [ + "arm64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/@vscode/vsce-sign-win32-x64": { + "version": "2.0.6", + "resolved": "https://registry.npmjs.org/@vscode/vsce-sign-win32-x64/-/vsce-sign-win32-x64-2.0.6.tgz", + "integrity": "sha512-mgth9Kvze+u8CruYMmhHw6Zgy3GRX2S+Ed5oSokDEK5vPEwGGKnmuXua9tmFhomeAnhgJnL4DCna3TiNuGrBTQ==", + "cpu": [ + "x64" + ], + "dev": true, + "optional": true, + "os": [ + "win32" + ] + }, + "node_modules/agent-base": { + "version": "7.1.4", + "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-7.1.4.tgz", + "integrity": "sha512-MnA+YT8fwfJPgBx3m60MNqakm30XOkyIoH1y6huTQvC0PwZG7ki8NacLBcrPbNoo8vEZy7Jpuk7+jMO+CUovTQ==", + "dev": true, + "engines": { + "node": ">= 14" + } + }, + "node_modules/ajv": { + "version": "8.20.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.20.0.tgz", + "integrity": "sha512-Thbli+OlOj+iMPYFBVBfJ3OmCAnaSyNn4M1vz9T6Gka5Jt9ba/HIR56joy65tY6kx/FCF5VXNB819Y7/GUrBGA==", + "dev": true, + "dependencies": { + "fast-deep-equal": "^3.1.3", + "fast-uri": "^3.0.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ansi-escapes": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-7.3.0.tgz", + "integrity": "sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==", + "dev": true, + "dependencies": { + "environment": "^1.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/ansi-regex": { + "version": "6.4.0", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-6.4.0.tgz", + "integrity": "sha512-KzTVk2tCWAHtYrvvvaP8bJKJq2pVinhLcGEQdtLIYPbmNGNyYe8QwNaTUYQp2J7/vIsUKt5QCqAfUkYyG9DkOw==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/ansi-regex?sponsor=1" + } + }, + "node_modules/ansi-styles": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", + "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", + "dev": true, + "dependencies": { + "color-convert": "^2.0.1" + }, + "engines": { + "node": ">=8" + }, + "funding": { + "url": "https://github.com/chalk/ansi-styles?sponsor=1" + } + }, + "node_modules/argparse": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/argparse/-/argparse-2.0.1.tgz", + "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", + "dev": true + }, + "node_modules/astral-regex": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/astral-regex/-/astral-regex-2.0.0.tgz", + "integrity": "sha512-Z7tMw1ytTXt5jqMcOP+OQteU1VuNK9Y02uuJtKQ1Sv69jXQKKg5cibLwGJow8yzZP+eAc18EmLGPal0bp36rvQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/asynckit": { + "version": "0.4.0", + "resolved": "https://registry.npmjs.org/asynckit/-/asynckit-0.4.0.tgz", + "integrity": "sha512-Oei9OH4tRh0YqU3GxhX79dM/mwVgvbZJaSNaRk+bshkj0S5cfHcgYakreBjrHwatXKbz+IoIdYLxrKim2MjW0Q==", + "dev": true + }, + "node_modules/azure-devops-node-api": { + "version": "12.5.0", + "resolved": "https://registry.npmjs.org/azure-devops-node-api/-/azure-devops-node-api-12.5.0.tgz", + "integrity": "sha512-R5eFskGvOm3U/GzeAuxRkUsAl0hrAwGgWn6zAd2KrZmrEhWZVqLew4OOupbQlXUuojUzpGtq62SmdhJ06N88og==", + "dev": true, + "dependencies": { + "tunnel": "0.0.6", + "typed-rest-client": "^1.8.4" + } + }, + "node_modules/balanced-match": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-4.0.4.tgz", + "integrity": "sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==", + "dev": true, + "engines": { + "node": "18 || 20 || >=22" + } + }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/binaryextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/binaryextensions/-/binaryextensions-6.11.0.tgz", + "integrity": "sha512-sXnYK/Ij80TO3lcqZVV2YgfKN5QjUWIRk/XSm2J/4bd/lPko3lvk0O4ZppH6m+6hB2/GTu+ptNwVFe1xh+QLQw==", + "dev": true, + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/bl": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/bl/-/bl-4.1.0.tgz", + "integrity": "sha512-1W07cM9gS6DcLperZfFSj+bWLtaPGSOHWhPiGzXmvVJbRLdG82sH/Kn8EtW1VqWVA54AKf2h5k5BbnIbwF3h6w==", + "dev": true, + "optional": true, + "dependencies": { + "buffer": "^5.5.0", + "inherits": "^2.0.4", + "readable-stream": "^3.4.0" + } + }, + "node_modules/boolbase": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/boolbase/-/boolbase-1.0.0.tgz", + "integrity": "sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww==", + "dev": true + }, + "node_modules/boundary": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/boundary/-/boundary-2.0.0.tgz", + "integrity": "sha512-rJKn5ooC9u8q13IMCrW0RSp31pxBCHE3y9V/tp3TdWSLf8Em3p6Di4NBpfzbJge9YjjFEsD0RtFEjtvHL5VyEA==", + "dev": true + }, + "node_modules/brace-expansion": { + "version": "5.0.12", + "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-5.0.12.tgz", + "integrity": "sha512-YovQ3rzhaLMIrDjNDMkNS01tea93qhEhG5xy8f6+R0l+dw3Ki+5sCoIoI942iuLZTHWogWktgwVDhU09iNEimQ==", + "dev": true, + "dependencies": { + "balanced-match": "^4.0.2" + }, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/braces": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz", + "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==", + "dev": true, + "dependencies": { + "fill-range": "^7.1.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/buffer": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-5.7.1.tgz", + "integrity": "sha512-EHcyIPBQ4BSGlvjB16k5KgAJ27CIsHY/2JBmCRReo48y9rQ3MaUzWX3KVlBa4U7MyX02HdVj0K7C3WaB3ju7FQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.1.13" + } + }, + "node_modules/buffer-crc32": { + "version": "0.2.13", + "resolved": "https://registry.npmjs.org/buffer-crc32/-/buffer-crc32-0.2.13.tgz", + "integrity": "sha512-VO9Ht/+p3SN7SKWqcrgEzjGbRSJYTx+Q1pTQC0wrWqHx0vpJraQ6GtHx8tvcg1rlK1byhU5gccxgOgj7B0TDkQ==", + "dev": true, + "engines": { + "node": "*" + } + }, + "node_modules/buffer-equal-constant-time": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/buffer-equal-constant-time/-/buffer-equal-constant-time-1.0.1.tgz", + "integrity": "sha512-zRpUiDwd/xk6ADqPMATG8vc9VPrkck7T07OIx0gnjmJAnHnTVXNQG3vfvWNuiZIkwu9KrKdA1iJKfsfTVxE6NA==", + "dev": true + }, + "node_modules/bundle-name": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.1.tgz", + "integrity": "sha512-DdH81/zPLVS11EUgWq3tEu/xn+EzljlMYooDNdzWEnFha3R3NBMpMV1UqYIpjYHV/SgpFKMIX1Oh7o06SjM/oA==", + "dev": true, + "dependencies": { + "run-applescript": "^7.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/call-bind-apply-helpers": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/call-bind-apply-helpers/-/call-bind-apply-helpers-1.0.2.tgz", + "integrity": "sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/call-bound": { + "version": "1.0.4", + "resolved": "https://registry.npmjs.org/call-bound/-/call-bound-1.0.4.tgz", + "integrity": "sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "get-intrinsic": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/chalk": { + "version": "4.1.2", + "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", + "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.1.0", + "supports-color": "^7.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/chalk?sponsor=1" + } + }, + "node_modules/cheerio": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/cheerio/-/cheerio-1.2.0.tgz", + "integrity": "sha512-WDrybc/gKFpTYQutKIK6UvfcuxijIZfMfXaYm8NMsPQxSYvf+13fXUJ4rztGGbJcBQ/GF55gvrZ0Bc0bj/mqvg==", + "dev": true, + "dependencies": { + "cheerio-select": "^2.1.0", + "dom-serializer": "^2.0.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "encoding-sniffer": "^0.2.1", + "htmlparser2": "^10.1.0", + "parse5": "^7.3.0", + "parse5-htmlparser2-tree-adapter": "^7.1.0", + "parse5-parser-stream": "^7.1.2", + "undici": "^7.19.0", + "whatwg-mimetype": "^4.0.0" + }, + "engines": { + "node": ">=20.18.1" + }, + "funding": { + "url": "https://github.com/cheeriojs/cheerio?sponsor=1" + } + }, + "node_modules/cheerio-select": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/cheerio-select/-/cheerio-select-2.1.0.tgz", + "integrity": "sha512-9v9kG0LvzrlcungtnJtpGNxY+fzECQKhK4EGJX2vByejiMX84MFNQw4UxPJl3bFbTMw+Dfs37XaIkCwTZfLh4g==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-select": "^5.1.0", + "css-what": "^6.1.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/chownr": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/chownr/-/chownr-1.1.4.tgz", + "integrity": "sha512-jJ0bqzaylmJtVnNgzTeSOs8DPavpbYgEr/b0YL8/2GO3xJEhInFmhKMUnEJQjZumK7KXGFhUy89PrsJWlakBVg==", + "dev": true, + "optional": true + }, + "node_modules/cockatiel": { + "version": "3.2.1", + "resolved": "https://registry.npmjs.org/cockatiel/-/cockatiel-3.2.1.tgz", + "integrity": "sha512-gfrHV6ZPkquExvMh9IOkKsBzNDk6sDuZ6DdBGUBkvFnTCqCxzpuq48RySgP0AnaqQkw2zynOFj9yly6T1Q2G5Q==", + "dev": true, + "engines": { + "node": ">=16" + } + }, + "node_modules/color-convert": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", + "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", + "dev": true, + "dependencies": { + "color-name": "~1.1.4" + }, + "engines": { + "node": ">=7.0.0" + } + }, + "node_modules/color-name": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", + "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", + "dev": true + }, + "node_modules/combined-stream": { + "version": "1.0.8", + "resolved": "https://registry.npmjs.org/combined-stream/-/combined-stream-1.0.8.tgz", + "integrity": "sha512-FQN4MRfuJeHf7cBbBMJFXhKSDq+2kAArBlmRBvcvFE5BB1HZKXtSFASDhdlz9zOYwxh8lDdnvmMOe/+5cdoEdg==", + "dev": true, + "dependencies": { + "delayed-stream": "~1.0.0" + }, + "engines": { + "node": ">= 0.8" + } + }, + "node_modules/commander": { + "version": "12.1.0", + "resolved": "https://registry.npmjs.org/commander/-/commander-12.1.0.tgz", + "integrity": "sha512-Vw8qHK3bZM9y/P10u3Vib8o/DdkvA2OtPtZvD871QKjy74Wj1WSKFILMPRPSdUSx5RFK1arlJzEtA4PkFgnbuA==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/css-select": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/css-select/-/css-select-5.2.2.tgz", + "integrity": "sha512-TizTzUddG/xYLA3NXodFM0fSbNizXjOKhqiQQwvhlspadZokn1KDy0NZFS0wuEubIYAV5/c1/lAr0TaaFXEXzw==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0", + "css-what": "^6.1.0", + "domhandler": "^5.0.2", + "domutils": "^3.0.1", + "nth-check": "^2.0.1" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/css-what": { + "version": "6.2.2", + "resolved": "https://registry.npmjs.org/css-what/-/css-what-6.2.2.tgz", + "integrity": "sha512-u/O3vwbptzhMs3L1fQE82ZSLHQQfto5gyZzwteVIEyeaY5Fc7R4dapF/BvRoSYFeqfBk4m0V1Vafq5Pjv25wvA==", + "dev": true, + "engines": { + "node": ">= 6" + }, + "funding": { + "url": "https://github.com/sponsors/fb55" + } + }, + "node_modules/debug": { + "version": "4.4.3", + "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.3.tgz", + "integrity": "sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==", + "dev": true, + "dependencies": { + "ms": "^2.1.3" + }, + "engines": { + "node": ">=6.0" + }, + "peerDependenciesMeta": { + "supports-color": { + "optional": true + } + } + }, + "node_modules/decompress-response": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/decompress-response/-/decompress-response-6.0.0.tgz", + "integrity": "sha512-aW35yZM6Bb/4oJlZncMH2LCoZtJXTRxES17vE3hoRiowU2kWHaJKFkSBDnDR+cm9J+9QhXmREyIfv0pji9ejCQ==", + "dev": true, + "optional": true, + "dependencies": { + "mimic-response": "^3.1.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/deep-extend": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", + "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", + "dev": true, + "optional": true, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/default-browser": { + "version": "5.5.1", + "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.1.tgz", + "integrity": "sha512-m1pAzaJgZ/gssEqlOhJkPJp8Xly7QyW6xcrkUa2KKcDeDSEMP7X8xipU3snUcfisTQx0w1AGae+9UtJSfVnXGw==", + "dev": true, + "dependencies": { + "bundle-name": "^4.1.0", + "default-browser-id": "^5.0.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/default-browser-id": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/default-browser-id/-/default-browser-id-5.0.1.tgz", + "integrity": "sha512-x1VCxdX4t+8wVfd1so/9w+vQ4vx7lKd2Qp5tDRutErwmR85OgmfX7RlLRMWafRMY7hbEiXIbudNrjOAPa/hL8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/define-lazy-prop": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/define-lazy-prop/-/define-lazy-prop-3.0.0.tgz", + "integrity": "sha512-N+MeXYoqr3pOgn8xfyRPREN7gHakLYjhsHhWGT3fWAiL4IkAt0iDw14QiiEm2bE30c5XX5q0FtAA3CK5f9/BUg==", + "dev": true, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/delayed-stream": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/delayed-stream/-/delayed-stream-1.0.0.tgz", + "integrity": "sha512-ZySD7Nf91aLB0RxL4KGrKHBXl7Eds1DAmEdcoVawXnLD7SDhpNgtuII2aAkg7a7QS41jxPSZ17p4VdGnMHk3MQ==", + "dev": true, + "engines": { + "node": ">=0.4.0" + } + }, + "node_modules/detect-libc": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/detect-libc/-/detect-libc-2.1.2.tgz", + "integrity": "sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/dom-serializer": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/dom-serializer/-/dom-serializer-2.0.0.tgz", + "integrity": "sha512-wIkAryiqt/nV5EQKqQpo3SToSOV9J0DnbJqwK7Wv/Trc92zIAYZ4FlMu+JPFW1DfGFt81ZTCGgDEabffXeLyJg==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.2", + "entities": "^4.2.0" + }, + "funding": { + "url": "https://github.com/cheeriojs/dom-serializer?sponsor=1" + } + }, + "node_modules/domelementtype": { + "version": "2.3.0", + "resolved": "https://registry.npmjs.org/domelementtype/-/domelementtype-2.3.0.tgz", + "integrity": "sha512-OLETBj6w0OsagBwdXnPdN0cnMfF9opN69co+7ZrbfPGrdpPVNBUj02spi6B1N7wChLQiPn4CSH/zJvXw56gmHw==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ] + }, + "node_modules/domhandler": { + "version": "5.0.3", + "resolved": "https://registry.npmjs.org/domhandler/-/domhandler-5.0.3.tgz", + "integrity": "sha512-cgwlv/1iFQiFnU96XXgROh8xTeetsnJiDsTc7TYCLFd9+/WNkIqPTxiM/8pSd8VIrhXGTf1Ny1q1hquVqDJB5w==", + "dev": true, + "dependencies": { + "domelementtype": "^2.3.0" + }, + "engines": { + "node": ">= 4" + }, + "funding": { + "url": "https://github.com/fb55/domhandler?sponsor=1" + } + }, + "node_modules/domutils": { + "version": "3.2.2", + "resolved": "https://registry.npmjs.org/domutils/-/domutils-3.2.2.tgz", + "integrity": "sha512-6kZKyUajlDuqlHKVX1w7gyslj9MPIXzIFiz/rGu35uC1wMi+kMhQwGhl4lt9unC9Vb9INnY9Z3/ZA3+FhASLaw==", + "dev": true, + "dependencies": { + "dom-serializer": "^2.0.0", + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3" + }, + "funding": { + "url": "https://github.com/fb55/domutils?sponsor=1" + } + }, + "node_modules/dunder-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/dunder-proto/-/dunder-proto-1.0.1.tgz", + "integrity": "sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.1", + "es-errors": "^1.3.0", + "gopd": "^1.2.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/ecdsa-sig-formatter": { + "version": "1.0.11", + "resolved": "https://registry.npmjs.org/ecdsa-sig-formatter/-/ecdsa-sig-formatter-1.0.11.tgz", + "integrity": "sha512-nagl3RYrbNv6kQkeJIpt6NJZy8twLB/2vtz6yN9Z4vRKHN4/QZJIEbqohALSgwKdnksuY3k5Addp5lg8sVoVcQ==", + "dev": true, + "dependencies": { + "safe-buffer": "^5.0.1" + } + }, + "node_modules/editions": { + "version": "6.22.0", + "resolved": "https://registry.npmjs.org/editions/-/editions-6.22.0.tgz", + "integrity": "sha512-UgGlf8IW75je7HZjNDpJdCv4cGJWIi6yumFdZ0R7A8/CIhQiWUjyGLCxdHpd8bmyD1gnkfUNK0oeOXqUS2cpfQ==", + "dev": true, + "dependencies": { + "version-range": "^4.15.0" + }, + "engines": { + "ecmascript": ">= es5", + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/emoji-regex": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/emoji-regex/-/emoji-regex-8.0.0.tgz", + "integrity": "sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==", + "dev": true + }, + "node_modules/encoding-sniffer": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/encoding-sniffer/-/encoding-sniffer-0.2.1.tgz", + "integrity": "sha512-5gvq20T6vfpekVtqrYQsSCFZ1wEg5+wW0/QaZMWkFr6BqD3NfKs0rLCx4rrVlSWJeZb5NBJgVLswK/w2MWU+Gw==", + "dev": true, + "dependencies": { + "iconv-lite": "^0.6.3", + "whatwg-encoding": "^3.1.1" + }, + "funding": { + "url": "https://github.com/fb55/encoding-sniffer?sponsor=1" + } + }, + "node_modules/end-of-stream": { + "version": "1.4.5", + "resolved": "https://registry.npmjs.org/end-of-stream/-/end-of-stream-1.4.5.tgz", + "integrity": "sha512-ooEGc6HP26xXq/N+GCGOT0JKCLDGrq2bQUZrQ7gyrJiZANJ/8YDTxTpQBXGMn+WbIQXNVpyWymm7KYVICQnyOg==", + "dev": true, + "optional": true, + "dependencies": { + "once": "^1.4.0" + } + }, + "node_modules/entities": { + "version": "4.5.0", + "resolved": "https://registry.npmjs.org/entities/-/entities-4.5.0.tgz", + "integrity": "sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/environment": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/environment/-/environment-1.1.0.tgz", + "integrity": "sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/es-define-property": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/es-define-property/-/es-define-property-1.0.1.tgz", + "integrity": "sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-errors": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/es-errors/-/es-errors-1.3.0.tgz", + "integrity": "sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-object-atoms": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.2.tgz", + "integrity": "sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/es-set-tostringtag": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/es-set-tostringtag/-/es-set-tostringtag-2.1.0.tgz", + "integrity": "sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.6", + "has-tostringtag": "^1.0.2", + "hasown": "^2.0.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/expand-template": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/expand-template/-/expand-template-2.0.3.tgz", + "integrity": "sha512-XYfuKMvj4O35f/pOXLObndIRvyQ+/+6AhODh+OKWj9S9498pHHn/IMszH+gt0fBCRWMNfk1ZSp5x3AifmnI2vg==", + "dev": true, + "optional": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/fast-deep-equal": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/fast-deep-equal/-/fast-deep-equal-3.1.3.tgz", + "integrity": "sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==", + "dev": true + }, + "node_modules/fast-glob": { + "version": "3.3.3", + "resolved": "https://registry.npmjs.org/fast-glob/-/fast-glob-3.3.3.tgz", + "integrity": "sha512-7MptL8U0cqcFdzIzwOTHoilX9x5BrNqye7Z/LuC7kCMRio1EMSyqRK3BEAUD7sXRq4iT4AzTVuZdhgQ2TCvYLg==", + "dev": true, + "dependencies": { + "@nodelib/fs.stat": "^2.0.2", + "@nodelib/fs.walk": "^1.2.3", + "glob-parent": "^5.1.2", + "merge2": "^1.3.0", + "micromatch": "^4.0.8" + }, + "engines": { + "node": ">=8.6.0" + } + }, + "node_modules/fast-uri": { + "version": "3.1.9", + "resolved": "https://registry.npmjs.org/fast-uri/-/fast-uri-3.1.9.tgz", + "integrity": "sha512-hS/0Wt3Ya043oKXs0sIxrMpKQITXhMLIrVAKKXi4gozGC9v7YRWVnXIZzjNPTO02M9BdxLCWqK1yRXgNziVyJQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/fastify" + }, + { + "type": "opencollective", + "url": "https://opencollective.com/fastify" + } + ] + }, + "node_modules/fastq": { + "version": "1.20.3", + "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.3.tgz", + "integrity": "sha512-XKv5nnLs6nLF71NgiKJLIZFLkPyIEuOselLG7ujZnGrRfQK8HpvY+WqKhAJUAdLomwVHErVS4LfxFlPq0/FTAw==", + "dev": true, + "dependencies": { + "reusify": "^1.0.4" + } + }, + "node_modules/fill-range": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz", + "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==", + "dev": true, + "dependencies": { + "to-regex-range": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/form-data": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.6.tgz", + "integrity": "sha512-vKatAh4SlVfgbv+YtmhiRjhEMJsYpsG1Y2rMQtR+SVSbytsSD1YGzDIcrAJmdFec88u/+VoGmxnl+80gL1tRCQ==", + "dev": true, + "dependencies": { + "asynckit": "^0.4.0", + "combined-stream": "^1.0.8", + "es-set-tostringtag": "^2.1.0", + "hasown": "^2.0.4", + "mime-types": "^2.1.35" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/fs-constants": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/fs-constants/-/fs-constants-1.0.0.tgz", + "integrity": "sha512-y6OAwoSIf7FyjMIv94u+b5rdheZEjzR63GTyZJm5qh4Bi+2YgwLCcI/fPFZkL5PSixOt6ZNKm+w+Hfp/Bciwow==", + "dev": true, + "optional": true + }, + "node_modules/fs-extra": { + "version": "11.4.1", + "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-11.4.1.tgz", + "integrity": "sha512-KYAb4c9BJQI6QqGKthV68OHe0badztdXJWKo0WtBA9IuCFPTKvE5ZdUBglP833aMjhaSPNO4A5j/EkzZtGlKjA==", + "dev": true, + "dependencies": { + "graceful-fs": "^4.2.0", + "jsonfile": "^6.0.1", + "universalify": "^2.0.0" + }, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/function-bind": { + "version": "1.1.2", + "resolved": "https://registry.npmjs.org/function-bind/-/function-bind-1.1.2.tgz", + "integrity": "sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==", + "dev": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-intrinsic": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/get-intrinsic/-/get-intrinsic-1.3.0.tgz", + "integrity": "sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==", + "dev": true, + "dependencies": { + "call-bind-apply-helpers": "^1.0.2", + "es-define-property": "^1.0.1", + "es-errors": "^1.3.0", + "es-object-atoms": "^1.1.1", + "function-bind": "^1.1.2", + "get-proto": "^1.0.1", + "gopd": "^1.2.0", + "has-symbols": "^1.1.0", + "hasown": "^2.0.2", + "math-intrinsics": "^1.1.0" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/get-proto": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/get-proto/-/get-proto-1.0.1.tgz", + "integrity": "sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==", + "dev": true, + "dependencies": { + "dunder-proto": "^1.0.1", + "es-object-atoms": "^1.0.0" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/github-from-package": { + "version": "0.0.0", + "resolved": "https://registry.npmjs.org/github-from-package/-/github-from-package-0.0.0.tgz", + "integrity": "sha512-SyHy3T1v2NUXn29OsWdxmK6RwHD+vkj3v8en8AOBZ1wBQ/hCAQ5bAQTD02kW4W9tUp/3Qh6J8r9EvntiyCmOOw==", + "dev": true, + "optional": true + }, + "node_modules/glob": { + "version": "13.0.6", + "resolved": "https://registry.npmjs.org/glob/-/glob-13.0.6.tgz", + "integrity": "sha512-Wjlyrolmm8uDpm/ogGyXZXb1Z+Ca2B8NbJwqBVg0axK9GbBeoS7yGV6vjXnYdGm6X53iehEuxxbyiKp8QmN4Vw==", + "dev": true, + "dependencies": { + "minimatch": "^10.2.2", + "minipass": "^7.1.3", + "path-scurry": "^2.0.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/glob-parent": { + "version": "5.1.2", + "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-5.1.2.tgz", + "integrity": "sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==", + "dev": true, + "dependencies": { + "is-glob": "^4.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/globby": { + "version": "14.1.0", + "resolved": "https://registry.npmjs.org/globby/-/globby-14.1.0.tgz", + "integrity": "sha512-0Ia46fDOaT7k4og1PDW4YbodWWr3scS2vAr2lTbsplOt2WkKp0vQbkI9wKis/T5LV/dqPjO3bpS/z6GTJB82LA==", + "dev": true, + "dependencies": { + "@sindresorhus/merge-streams": "^2.1.0", + "fast-glob": "^3.3.3", + "ignore": "^7.0.3", + "path-type": "^6.0.0", + "slash": "^5.1.0", + "unicorn-magic": "^0.3.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/gopd": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/gopd/-/gopd-1.2.0.tgz", + "integrity": "sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/graceful-fs": { + "version": "4.2.11", + "resolved": "https://registry.npmjs.org/graceful-fs/-/graceful-fs-4.2.11.tgz", + "integrity": "sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==", + "dev": true + }, + "node_modules/has-flag": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", + "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/has-symbols": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/has-symbols/-/has-symbols-1.1.0.tgz", + "integrity": "sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/has-tostringtag": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/has-tostringtag/-/has-tostringtag-1.0.2.tgz", + "integrity": "sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==", + "dev": true, + "dependencies": { + "has-symbols": "^1.0.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/hasown": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.4.tgz", + "integrity": "sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==", + "dev": true, + "dependencies": { + "function-bind": "^1.1.2" + }, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/hosted-git-info": { + "version": "4.1.0", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-4.1.0.tgz", + "integrity": "sha512-kyCuEOWjJqZuDbRHzL8V93NzQhwIB71oFWSyzVo+KPZI+pnQPPxucdkrOZvkLRnrf5URsQM+IJ09Dw29cRALIA==", + "dev": true, + "dependencies": { + "lru-cache": "^6.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/htmlparser2": { + "version": "10.1.0", + "resolved": "https://registry.npmjs.org/htmlparser2/-/htmlparser2-10.1.0.tgz", + "integrity": "sha512-VTZkM9GWRAtEpveh7MSF6SjjrpNVNNVJfFup7xTY3UpFtm67foy9HDVXneLtFVt4pMz5kZtgNcvCniNFb1hlEQ==", + "dev": true, + "funding": [ + "https://github.com/fb55/htmlparser2?sponsor=1", + { + "type": "github", + "url": "https://github.com/sponsors/fb55" + } + ], + "dependencies": { + "domelementtype": "^2.3.0", + "domhandler": "^5.0.3", + "domutils": "^3.2.2", + "entities": "^7.0.1" + } + }, + "node_modules/htmlparser2/node_modules/entities": { + "version": "7.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-7.0.1.tgz", + "integrity": "sha512-TWrgLOFUQTH994YUyl1yT4uyavY5nNB5muff+RtWaqNVCAK408b5ZnnbNAUEWLTCpum9w6arT70i1XdQ4UeOPA==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/http-proxy-agent": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/http-proxy-agent/-/http-proxy-agent-7.0.2.tgz", + "integrity": "sha512-T1gkAiYYDWYx3V5Bmyu7HcfcvL7mUrTWiM6yOfa3PIphViJ/gFPbvidQ+veqSOHci/PxBcDabeUNCzpOODJZig==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.0", + "debug": "^4.3.4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/https-proxy-agent": { + "version": "7.0.6", + "resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz", + "integrity": "sha512-vK9P5/iUfdl95AI+JVyUuIcVtd4ofvtrOr3HNtM2yxC9bnMbEdp3x01OhQNnjb8IJYi38VlTE3mBXwcfvywuSw==", + "dev": true, + "dependencies": { + "agent-base": "^7.1.2", + "debug": "4" + }, + "engines": { + "node": ">= 14" + } + }, + "node_modules/iconv-lite": { + "version": "0.6.3", + "resolved": "https://registry.npmjs.org/iconv-lite/-/iconv-lite-0.6.3.tgz", + "integrity": "sha512-4fCk79wshMdzMp2rH06qWrJE4iolqLhCUH+OiuIgU++RB0+94NlDL81atO7GX55uUKueo0txHNtvEyI6D7WdMw==", + "dev": true, + "dependencies": { + "safer-buffer": ">= 2.1.2 < 3.0.0" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/ignore": { + "version": "7.0.14", + "resolved": "https://registry.npmjs.org/ignore/-/ignore-7.0.14.tgz", + "integrity": "sha512-wCBiZ77u1tuWde6G/nfOtLaCTYcFDU5yCzARzK10y+rLhzYoL7a7yEemcopcc2Mzow7gZOIjnV2ym1NYmdiJOQ==", + "dev": true, + "engines": { + "node": ">= 4" + } + }, + "node_modules/index-to-position": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/index-to-position/-/index-to-position-1.2.0.tgz", + "integrity": "sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/inherits": { + "version": "2.0.4", + "resolved": "https://registry.npmjs.org/inherits/-/inherits-2.0.4.tgz", + "integrity": "sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==", + "dev": true, + "optional": true + }, + "node_modules/ini": { + "version": "1.3.8", + "resolved": "https://registry.npmjs.org/ini/-/ini-1.3.8.tgz", + "integrity": "sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==", + "dev": true, + "optional": true + }, + "node_modules/is-docker": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-docker/-/is-docker-3.0.0.tgz", + "integrity": "sha512-eljcgEDlEns/7AXFosB5K/2nCM4P7FQPkGc/DWLy5rmFEWvZayGrik1d9/QIY5nJ4f9YsVvBkA6kJpHn9rISdQ==", + "dev": true, + "bin": { + "is-docker": "cli.js" + }, + "engines": { + "node": "^12.20.0 || ^14.13.1 || >=16.0.0" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-extglob": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/is-extglob/-/is-extglob-2.1.1.tgz", + "integrity": "sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-fullwidth-code-point": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/is-fullwidth-code-point/-/is-fullwidth-code-point-3.0.0.tgz", + "integrity": "sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/is-glob": { + "version": "4.0.3", + "resolved": "https://registry.npmjs.org/is-glob/-/is-glob-4.0.3.tgz", + "integrity": "sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==", + "dev": true, + "dependencies": { + "is-extglob": "^2.1.1" + }, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/is-in-ssh": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-in-ssh/-/is-in-ssh-1.0.0.tgz", + "integrity": "sha512-jYa6Q9rH90kR1vKB6NM7qqd1mge3Fx4Dhw5TVlK1MUBqhEOuCagrEHMevNuCcbECmXZ0ThXkRm+Ymr51HwEPAw==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-inside-container": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/is-inside-container/-/is-inside-container-1.0.0.tgz", + "integrity": "sha512-KIYLCCJghfHZxqjYBE7rEy0OBuTd5xCHS7tHVgvCLkx7StIoaxwNW3hCALgEUjFfeRk+MG/Qxmp/vtETEF3tRA==", + "dev": true, + "dependencies": { + "is-docker": "^3.0.0" + }, + "bin": { + "is-inside-container": "cli.js" + }, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/is-number": { + "version": "7.0.0", + "resolved": "https://registry.npmjs.org/is-number/-/is-number-7.0.0.tgz", + "integrity": "sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==", + "dev": true, + "engines": { + "node": ">=0.12.0" + } + }, + "node_modules/is-wsl": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/is-wsl/-/is-wsl-3.1.1.tgz", + "integrity": "sha512-e6rvdUCiQCAuumZslxRJWR/Doq4VpPR82kqclvcS0efgt430SlGIk05vdCN58+VrzgtIcfNODjozVielycD4Sw==", + "dev": true, + "dependencies": { + "is-inside-container": "^1.0.0" + }, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/istextorbinary": { + "version": "9.5.0", + "resolved": "https://registry.npmjs.org/istextorbinary/-/istextorbinary-9.5.0.tgz", + "integrity": "sha512-5mbUj3SiZXCuRf9fT3ibzbSSEWiy63gFfksmGfdOzujPjW3k+z8WvIBxcJHBoQNlaZaiyB25deviif2+osLmLw==", + "dev": true, + "dependencies": { + "binaryextensions": "^6.11.0", + "editions": "^6.21.0", + "textextensions": "^6.11.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/js-tokens": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", + "integrity": "sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==", + "dev": true + }, + "node_modules/js-yaml": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.3.2.tgz", + "integrity": "sha512-SFNOvSJ+Dgf/9An904Yx+CgSlIPCkIpao4qo51lpee25TIRejdH3rhR4EZMGoNx3/TP3O+wzWuiTFl4sqbltzA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/nodeca" + } + ], + "dependencies": { + "argparse": "^2.0.1" + }, + "bin": { + "js-yaml": "bin/js-yaml.js" + } + }, + "node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==", + "dev": true + }, + "node_modules/json5": { + "version": "2.2.3", + "resolved": "https://registry.npmjs.org/json5/-/json5-2.2.3.tgz", + "integrity": "sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==", + "dev": true, + "bin": { + "json5": "lib/cli.js" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/jsonc-parser": { + "version": "3.3.1", + "resolved": "https://registry.npmjs.org/jsonc-parser/-/jsonc-parser-3.3.1.tgz", + "integrity": "sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==", + "dev": true + }, + "node_modules/jsonfile": { + "version": "6.2.1", + "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-6.2.1.tgz", + "integrity": "sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==", + "dev": true, + "dependencies": { + "universalify": "^2.0.0" + }, + "optionalDependencies": { + "graceful-fs": "^4.1.6" + } + }, + "node_modules/jsonwebtoken": { + "version": "9.0.3", + "resolved": "https://registry.npmjs.org/jsonwebtoken/-/jsonwebtoken-9.0.3.tgz", + "integrity": "sha512-MT/xP0CrubFRNLNKvxJ2BYfy53Zkm++5bX9dtuPbqAeQpTVe0MQTFhao8+Cp//EmJp244xt6Drw/GVEGCUj40g==", + "dev": true, + "dependencies": { + "jws": "^4.0.1", + "lodash.includes": "^4.3.0", + "lodash.isboolean": "^3.0.3", + "lodash.isinteger": "^4.0.4", + "lodash.isnumber": "^3.0.3", + "lodash.isplainobject": "^4.0.6", + "lodash.isstring": "^4.0.1", + "lodash.once": "^4.0.0", + "ms": "^2.1.1", + "semver": "^7.5.4" + }, + "engines": { + "node": ">=12", + "npm": ">=6" + } + }, + "node_modules/jwa": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/jwa/-/jwa-2.0.1.tgz", + "integrity": "sha512-hRF04fqJIP8Abbkq5NKGN0Bbr3JxlQ+qhZufXVr0DvujKy93ZCbXZMHDL4EOtodSbCWxOqR8MS1tXA5hwqCXDg==", + "dev": true, + "dependencies": { + "buffer-equal-constant-time": "^1.0.1", + "ecdsa-sig-formatter": "1.0.11", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/jws": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/jws/-/jws-4.0.1.tgz", + "integrity": "sha512-EKI/M/yqPncGUUh44xz0PxSidXFr/+r0pA70+gIYhjv+et7yxM+s29Y+VGDkovRofQem0fs7Uvf4+YmAdyRduA==", + "dev": true, + "dependencies": { + "jwa": "^2.0.1", + "safe-buffer": "^5.0.1" + } + }, + "node_modules/keytar": { + "version": "7.9.0", + "resolved": "https://registry.npmjs.org/keytar/-/keytar-7.9.0.tgz", + "integrity": "sha512-VPD8mtVtm5JNtA2AErl6Chp06JBfy7diFQ7TQQhdpWOl6MrCRB+eRbvAZUsbGQS9kiMq0coJsy0W0vHpDCkWsQ==", + "dev": true, + "hasInstallScript": true, + "optional": true, + "dependencies": { + "node-addon-api": "^4.3.0", + "prebuild-install": "^7.0.1" + } + }, + "node_modules/leven": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/leven/-/leven-3.1.0.tgz", + "integrity": "sha512-qsda+H8jTaUaN/x5vzW2rzc+8Rw4TAQ/4KjB46IwK5VH+IlVeeeje/EoZRpiXvIqjFgK84QffqPztGI3VBLG1A==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/linkify-it": { + "version": "5.0.2", + "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-5.0.2.tgz", + "integrity": "sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "dependencies": { + "uc.micro": "^2.0.0" + } + }, + "node_modules/lodash": { + "version": "4.18.1", + "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.18.1.tgz", + "integrity": "sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==", + "dev": true + }, + "node_modules/lodash.includes": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/lodash.includes/-/lodash.includes-4.3.0.tgz", + "integrity": "sha512-W3Bx6mdkRTGtlJISOvVD/lbqjTlPPUDTMnlXZFnVwi9NKJ6tiAk6LVdlhZMm17VZisqhKcgzpO5Wz91PCt5b0w==", + "dev": true + }, + "node_modules/lodash.isboolean": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isboolean/-/lodash.isboolean-3.0.3.tgz", + "integrity": "sha512-Bz5mupy2SVbPHURB98VAcw+aHh4vRV5IPNhILUCsOzRmsTmSQ17jIuqopAentWoehktxGd9e/hbIXq980/1QJg==", + "dev": true + }, + "node_modules/lodash.isinteger": { + "version": "4.0.4", + "resolved": "https://registry.npmjs.org/lodash.isinteger/-/lodash.isinteger-4.0.4.tgz", + "integrity": "sha512-DBwtEWN2caHQ9/imiNeEA5ys1JoRtRfY3d7V9wkqtbycnAmTvRRmbHKDV4a0EYc678/dia0jrte4tjYwVBaZUA==", + "dev": true + }, + "node_modules/lodash.isnumber": { + "version": "3.0.3", + "resolved": "https://registry.npmjs.org/lodash.isnumber/-/lodash.isnumber-3.0.3.tgz", + "integrity": "sha512-QYqzpfwO3/CWf3XP+Z+tkQsfaLL/EnUlXWVkIk5FUPc4sBdTehEqZONuyRt2P67PXAk+NXmTBcc97zw9t1FQrw==", + "dev": true + }, + "node_modules/lodash.isplainobject": { + "version": "4.0.6", + "resolved": "https://registry.npmjs.org/lodash.isplainobject/-/lodash.isplainobject-4.0.6.tgz", + "integrity": "sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==", + "dev": true + }, + "node_modules/lodash.isstring": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/lodash.isstring/-/lodash.isstring-4.0.1.tgz", + "integrity": "sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==", + "dev": true + }, + "node_modules/lodash.once": { + "version": "4.1.1", + "resolved": "https://registry.npmjs.org/lodash.once/-/lodash.once-4.1.1.tgz", + "integrity": "sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg==", + "dev": true + }, + "node_modules/lodash.truncate": { + "version": "4.4.2", + "resolved": "https://registry.npmjs.org/lodash.truncate/-/lodash.truncate-4.4.2.tgz", + "integrity": "sha512-jttmRe7bRse52OsWIMDLaXxWqRAmtIUccAQ3garviCqJjafXOfNMO0yMfNpdD6zbGaTU0P5Nz7e7gAT6cKmJRw==", + "dev": true + }, + "node_modules/lru-cache": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-6.0.0.tgz", + "integrity": "sha512-Jo6dJ04CmSjuznwJSS3pUeWmd/H0ffTlkXXgwZi+eq1UCmqQwCh+eLsYOYCwY991i2Fah4h1BEMCx4qThGbsiA==", + "dev": true, + "dependencies": { + "yallist": "^4.0.0" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/markdown-it": { + "version": "14.3.3", + "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-14.3.3.tgz", + "integrity": "sha512-L//tDAkjTjJCU6pBUSBTgs3Kg4InVaZuUZog/b6V12UKhcDQ7S78877ibbCanRExwPuYP1CZD7Gd2dxSy1GXRg==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/puzrin" + }, + { + "type": "github", + "url": "https://github.com/sponsors/markdown-it" + } + ], + "dependencies": { + "argparse": "^2.0.1", + "entities": "^4.5.0", + "linkify-it": "^5.0.2", + "mdurl": "^2.0.0", + "punycode.js": "^2.3.1", + "uc.micro": "^2.1.0" + }, + "bin": { + "markdown-it": "bin/markdown-it.mjs" + } + }, + "node_modules/math-intrinsics": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/math-intrinsics/-/math-intrinsics-1.1.0.tgz", + "integrity": "sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==", + "dev": true, + "engines": { + "node": ">= 0.4" + } + }, + "node_modules/mdurl": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-2.1.0.tgz", + "integrity": "sha512-1+HBaOx0zi/dQWht8rNv9MYf9qqpqL/kxI0hXImU6Y547zM6Sni8BQibt7ifgMcYtQg41ao3Ivd6cnSM86inpg==", + "dev": true + }, + "node_modules/merge2": { + "version": "1.4.1", + "resolved": "https://registry.npmjs.org/merge2/-/merge2-1.4.1.tgz", + "integrity": "sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==", + "dev": true, + "engines": { + "node": ">= 8" + } + }, + "node_modules/micromatch": { + "version": "4.0.8", + "resolved": "https://registry.npmjs.org/micromatch/-/micromatch-4.0.8.tgz", + "integrity": "sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==", + "dev": true, + "dependencies": { + "braces": "^3.0.3", + "picomatch": "^2.3.1" + }, + "engines": { + "node": ">=8.6" + } + }, + "node_modules/mime": { + "version": "1.6.0", + "resolved": "https://registry.npmjs.org/mime/-/mime-1.6.0.tgz", + "integrity": "sha512-x0Vn8spI+wuJ1O6S7gnbaQg8Pxh4NNHb7KSINmEWKiPE4RKOplvijn+NkmYmmRgP68mc70j2EbeTFRsrswaQeg==", + "dev": true, + "bin": { + "mime": "cli.js" + }, + "engines": { + "node": ">=4" + } + }, + "node_modules/mime-db": { + "version": "1.52.0", + "resolved": "https://registry.npmjs.org/mime-db/-/mime-db-1.52.0.tgz", + "integrity": "sha512-sPU4uV7dYlvtWJxwwxHD0PuihVNiE7TyAbQ5SWxDCB9mUYvOgroQOwYQQOKPJ8CIbE+1ETVlOoK1UC2nU3gYvg==", + "dev": true, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mime-types": { + "version": "2.1.35", + "resolved": "https://registry.npmjs.org/mime-types/-/mime-types-2.1.35.tgz", + "integrity": "sha512-ZDY+bPm5zTTF+YpCrAU9nK0UgICYPT0QtT1NZWFv4s++TNkcgVaT0g6+4R2uI4MjQjzysHB1zxuWL50hzaeXiw==", + "dev": true, + "dependencies": { + "mime-db": "1.52.0" + }, + "engines": { + "node": ">= 0.6" + } + }, + "node_modules/mimic-response": { + "version": "3.1.0", + "resolved": "https://registry.npmjs.org/mimic-response/-/mimic-response-3.1.0.tgz", + "integrity": "sha512-z0yWI+4FDrrweS8Zmt4Ej5HdJmky15+L2e6Wgn3+iK5fWzb6T3fhNFq2+MeTRb064c6Wr4N/wv0DzQTjNzHNGQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/minimatch": { + "version": "10.2.6", + "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-10.2.6.tgz", + "integrity": "sha512-vpLQEs+VLCr1nU0BXS07maYoFwlDAH0gngQuuttxIwutDFEMHq2blX+8vpgxDdK3J1PwjCJiep77OitTZ4Ll1A==", + "dev": true, + "dependencies": { + "brace-expansion": "^5.0.8" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/minimist": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.8.tgz", + "integrity": "sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==", + "dev": true, + "optional": true, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/minipass": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/minipass/-/minipass-7.1.3.tgz", + "integrity": "sha512-tEBHqDnIoM/1rXME1zgka9g6Q2lcoCkxHLuc7ODJ5BxbP5d4c2Z5cGgtXAku59200Cx7diuHTOYfSBD8n6mm8A==", + "dev": true, + "engines": { + "node": ">=16 || 14 >=14.17" + } + }, + "node_modules/mkdirp-classic": { + "version": "0.5.3", + "resolved": "https://registry.npmjs.org/mkdirp-classic/-/mkdirp-classic-0.5.3.tgz", + "integrity": "sha512-gKLcREMhtuZRwRAfqP3RFW+TK4JqApVBtOIftVgjuABpAtpxhPGaDcfvbhNvD0B8iD1oUr/txX35NjcaY6Ns/A==", + "dev": true, + "optional": true + }, + "node_modules/ms": { + "version": "2.1.3", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.3.tgz", + "integrity": "sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==", + "dev": true + }, + "node_modules/mute-stream": { + "version": "0.0.8", + "resolved": "https://registry.npmjs.org/mute-stream/-/mute-stream-0.0.8.tgz", + "integrity": "sha512-nnbWWOkoWyUsTjKrhgD0dcz22mdkSnpYqbEjIm2nhwhuxlSkpywJmBo8h0ZqJdkp73mb90SssHkN4rsRaBAfAA==", + "dev": true + }, + "node_modules/napi-build-utils": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/napi-build-utils/-/napi-build-utils-2.0.0.tgz", + "integrity": "sha512-GEbrYkbfF7MoNaoh2iGG84Mnf/WZfB0GdGEsM8wz7Expx/LlWf5U8t9nvJKXSp3qr5IsEbK04cBGhol/KwOsWA==", + "dev": true, + "optional": true + }, + "node_modules/node-abi": { + "version": "3.101.0", + "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.101.0.tgz", + "integrity": "sha512-bkb/RkuWCE64eWu1PCh3Fu2pGc9mKRs+kOE+FU2bGHRYlQa86/D321DSATV54AsCQMKz3/xSt9sRgraZTpqdhQ==", + "dev": true, + "optional": true, + "dependencies": { + "semver": "^7.3.5" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/node-addon-api": { + "version": "4.3.0", + "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-4.3.0.tgz", + "integrity": "sha512-73sE9+3UaLYYFmDsFZnqCInzPyh3MqIwZO9cw58yIqAZhONrrabrYyYe3TuIqtIiOuTXVhsGau8hcrhhwSsDIQ==", + "dev": true, + "optional": true + }, + "node_modules/node-sarif-builder": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/node-sarif-builder/-/node-sarif-builder-3.4.0.tgz", + "integrity": "sha512-tGnJW6OKRii9u/b2WiUViTJS+h7Apxx17qsMUjsUeNDiMMX5ZFf8F8Fcz7PAQ6omvOxHZtvDTmOYKJQwmfpjeg==", + "dev": true, + "dependencies": { + "@types/sarif": "^2.1.7", + "fs-extra": "^11.1.1" + }, + "engines": { + "node": ">=20" + } + }, + "node_modules/normalize-package-data": { + "version": "6.0.2", + "resolved": "https://registry.npmjs.org/normalize-package-data/-/normalize-package-data-6.0.2.tgz", + "integrity": "sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==", + "dev": true, + "dependencies": { + "hosted-git-info": "^7.0.0", + "semver": "^7.3.5", + "validate-npm-package-license": "^3.0.4" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/hosted-git-info": { + "version": "7.0.2", + "resolved": "https://registry.npmjs.org/hosted-git-info/-/hosted-git-info-7.0.2.tgz", + "integrity": "sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==", + "dev": true, + "dependencies": { + "lru-cache": "^10.0.1" + }, + "engines": { + "node": "^16.14.0 || >=18.0.0" + } + }, + "node_modules/normalize-package-data/node_modules/lru-cache": { + "version": "10.4.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-10.4.3.tgz", + "integrity": "sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==", + "dev": true + }, + "node_modules/nth-check": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/nth-check/-/nth-check-2.1.1.tgz", + "integrity": "sha512-lqjrjmaOoAnWfMmBPL+XNnynZh2+swxiX3WUE0s4yEHI6m+AwrK2UZOimIRl3X/4QctVqS8AiZjFqyOGrMXb/w==", + "dev": true, + "dependencies": { + "boolbase": "^1.0.0" + }, + "funding": { + "url": "https://github.com/fb55/nth-check?sponsor=1" + } + }, + "node_modules/object-inspect": { + "version": "1.13.4", + "resolved": "https://registry.npmjs.org/object-inspect/-/object-inspect-1.13.4.tgz", + "integrity": "sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==", + "dev": true, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/once": { + "version": "1.4.0", + "resolved": "https://registry.npmjs.org/once/-/once-1.4.0.tgz", + "integrity": "sha512-lNaJgI+2Q5URQBkccEKHTQOPaXdUxnZZElQTZY0MFUAuaEqe1E+Nyvgdz/aIyNi6Z9MzO5dv1H8n58/GELp3+w==", + "dev": true, + "optional": true, + "dependencies": { + "wrappy": "1" + } + }, + "node_modules/open": { + "version": "11.0.4", + "resolved": "https://registry.npmjs.org/open/-/open-11.0.4.tgz", + "integrity": "sha512-++Zlftm0kVLPmzC06t6epuWmcRMDbI4z5P3NNX979WA/k23+NtSOynEGzsVfZwguKw2mi5umVgnBlJQMwRz4Pg==", + "dev": true, + "dependencies": { + "default-browser": "^5.5.1", + "define-lazy-prop": "^3.0.0", + "is-in-ssh": "^1.0.0", + "is-inside-container": "^1.0.0", + "powershell-utils": "^0.2.1", + "wsl-utils": "^1.0.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/p-map": { + "version": "7.1.1", + "resolved": "https://registry.npmjs.org/p-map/-/p-map-7.1.1.tgz", + "integrity": "sha512-6Lh3jQPLthYujSrlw6ccWXBGZTkoTCz27Hd92g79LH/kjAbgoEu0t442cCCNbegPSJsA0lwDX8z0bcKtkpA32g==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-json": { + "version": "8.3.0", + "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-8.3.0.tgz", + "integrity": "sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==", + "dev": true, + "dependencies": { + "@babel/code-frame": "^7.26.2", + "index-to-position": "^1.1.0", + "type-fest": "^4.39.1" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/parse-semver": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/parse-semver/-/parse-semver-1.1.1.tgz", + "integrity": "sha512-Eg1OuNntBMH0ojvEKSrvDSnwLmvVuUOSdylH/pSCPNMIspLlweJyIWXCE+k/5hm3cj/EBUYwmWkjhBALNP4LXQ==", + "dev": true, + "dependencies": { + "semver": "^5.1.0" + } + }, + "node_modules/parse-semver/node_modules/semver": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/semver/-/semver-5.7.2.tgz", + "integrity": "sha512-cBznnQ9KjJqU67B52RMC65CMarK2600WFnbkcaiwWq3xy/5haFJlshgnpjovMVJ+Hff49d8GEn0b87C5pDQ10g==", + "dev": true, + "bin": { + "semver": "bin/semver" + } + }, + "node_modules/parse5": { + "version": "7.3.0", + "resolved": "https://registry.npmjs.org/parse5/-/parse5-7.3.0.tgz", + "integrity": "sha512-IInvU7fabl34qmi9gY8XOVxhYyMyuH2xUNpb2q8/Y+7552KlejkRvqvD19nMoUW/uQGGbqNpA6Tufu5FL5BZgw==", + "dev": true, + "dependencies": { + "entities": "^6.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-htmlparser2-tree-adapter": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/parse5-htmlparser2-tree-adapter/-/parse5-htmlparser2-tree-adapter-7.1.0.tgz", + "integrity": "sha512-ruw5xyKs6lrpo9x9rCZqZZnIUntICjQAd0Wsmp396Ul9lN/h+ifgVV1x1gZHi8euej6wTfpqX8j+BFQxF0NS/g==", + "dev": true, + "dependencies": { + "domhandler": "^5.0.3", + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5-parser-stream": { + "version": "7.1.2", + "resolved": "https://registry.npmjs.org/parse5-parser-stream/-/parse5-parser-stream-7.1.2.tgz", + "integrity": "sha512-JyeQc9iwFLn5TbvvqACIF/VXG6abODeB3Fwmv/TGdLk2LfbWkaySGY72at4+Ty7EkPZj854u4CrICqNk2qIbow==", + "dev": true, + "dependencies": { + "parse5": "^7.0.0" + }, + "funding": { + "url": "https://github.com/inikulin/parse5?sponsor=1" + } + }, + "node_modules/parse5/node_modules/entities": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/entities/-/entities-6.0.1.tgz", + "integrity": "sha512-aN97NXWF6AWBTahfVOIrB/NShkzi5H7F9r1s9mD3cDj4Ko5f2qhhVoYMibXF7GlLveb/D2ioWay8lxI97Ven3g==", + "dev": true, + "engines": { + "node": ">=0.12" + }, + "funding": { + "url": "https://github.com/fb55/entities?sponsor=1" + } + }, + "node_modules/path-scurry": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/path-scurry/-/path-scurry-2.0.2.tgz", + "integrity": "sha512-3O/iVVsJAPsOnpwWIeD+d6z/7PmqApyQePUtCndjatj/9I5LylHvt5qluFaBT3I5h3r1ejfR056c+FCv+NnNXg==", + "dev": true, + "dependencies": { + "lru-cache": "^11.0.0", + "minipass": "^7.1.2" + }, + "engines": { + "node": "18 || 20 || >=22" + }, + "funding": { + "url": "https://github.com/sponsors/isaacs" + } + }, + "node_modules/path-scurry/node_modules/lru-cache": { + "version": "11.5.3", + "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-11.5.3.tgz", + "integrity": "sha512-U4N8FgzmWxc8k1VH8Kr6lQg18U7Fjvby6wXHVRX/ZZ7IwWbRMgrRbP0Wrb5q5NVinryp4SQampHKdvtecItxUg==", + "dev": true, + "engines": { + "node": "20 || >=22" + } + }, + "node_modules/path-type": { + "version": "6.0.0", + "resolved": "https://registry.npmjs.org/path-type/-/path-type-6.0.0.tgz", + "integrity": "sha512-Vj7sf++t5pBD637NSfkxpHSMfWaeig5+DKWLhcqIYx6mWQz5hdJTGDVMQiJcw1ZYkhs7AazKDGpRVji1LJCZUQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/pend": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/pend/-/pend-1.2.0.tgz", + "integrity": "sha512-F3asv42UuXchdzt+xXqfW1OGlVBe+mxa2mqI0pg5yAHZPvFmY3Y6drSf/GQ1A86WgWEN9Kzh/WrgKa6iGcHXLg==", + "dev": true + }, + "node_modules/picocolors": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/picocolors/-/picocolors-1.1.1.tgz", + "integrity": "sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==", + "dev": true + }, + "node_modules/picomatch": { + "version": "2.3.2", + "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.2.tgz", + "integrity": "sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==", + "dev": true, + "engines": { + "node": ">=8.6" + }, + "funding": { + "url": "https://github.com/sponsors/jonschlinkert" + } + }, + "node_modules/pluralize": { + "version": "8.0.0", + "resolved": "https://registry.npmjs.org/pluralize/-/pluralize-8.0.0.tgz", + "integrity": "sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==", + "dev": true, + "engines": { + "node": ">=4" + } + }, + "node_modules/powershell-utils": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/powershell-utils/-/powershell-utils-0.2.1.tgz", + "integrity": "sha512-C+y9x90UElAddDZmV4qOx9W53B61PO7cIqWz2dQsWlwswuq4mr8NEwytdGKboYbQlGZ3awrkTeNvcZiZNHnQ8A==", + "dev": true, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/prebuild-install": { + "version": "7.1.3", + "resolved": "https://registry.npmjs.org/prebuild-install/-/prebuild-install-7.1.3.tgz", + "integrity": "sha512-8Mf2cbV7x1cXPUILADGI3wuhfqWvtiLA1iclTDbFRZkgRQS0NqsPZphna9V+HyTEadheuPmjaJMsbzKQFOzLug==", + "deprecated": "No longer maintained. Please contact the author of the relevant native addon; alternatives are available.", + "dev": true, + "optional": true, + "dependencies": { + "detect-libc": "^2.0.0", + "expand-template": "^2.0.3", + "github-from-package": "0.0.0", + "minimist": "^1.2.3", + "mkdirp-classic": "^0.5.3", + "napi-build-utils": "^2.0.0", + "node-abi": "^3.3.0", + "pump": "^3.0.0", + "rc": "^1.2.7", + "simple-get": "^4.0.0", + "tar-fs": "^2.0.0", + "tunnel-agent": "^0.6.0" + }, + "bin": { + "prebuild-install": "bin.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/pump": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/pump/-/pump-3.0.4.tgz", + "integrity": "sha512-VS7sjc6KR7e1ukRFhQSY5LM2uBWAUPiOPa/A3mkKmiMwSmRFUITt0xuj+/lesgnCv+dPIEYlkzrcyXgquIHMcA==", + "dev": true, + "optional": true, + "dependencies": { + "end-of-stream": "^1.1.0", + "once": "^1.3.1" + } + }, + "node_modules/punycode.js": { + "version": "2.3.1", + "resolved": "https://registry.npmjs.org/punycode.js/-/punycode.js-2.3.1.tgz", + "integrity": "sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==", + "dev": true, + "engines": { + "node": ">=6" + } + }, + "node_modules/qs": { + "version": "6.16.1", + "resolved": "https://registry.npmjs.org/qs/-/qs-6.16.1.tgz", + "integrity": "sha512-5Px6J7rfHBAGUhNQ3es4YzcOdBBw0SDcVtThJml4RdnKw4tKs2epA0kPZ+K8X+WiHktT93RHVcQ/3ym5aTCgtg==", + "dev": true, + "dependencies": { + "es-define-property": "^1.0.1", + "side-channel": "^1.1.1" + }, + "engines": { + "node": ">=0.6" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/queue-microtask": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/queue-microtask/-/queue-microtask-1.2.3.tgz", + "integrity": "sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/rc": { + "version": "1.2.8", + "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", + "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", + "dev": true, + "optional": true, + "dependencies": { + "deep-extend": "^0.6.0", + "ini": "~1.3.0", + "minimist": "^1.2.0", + "strip-json-comments": "~2.0.1" + }, + "bin": { + "rc": "cli.js" + } + }, + "node_modules/rc-config-loader": { + "version": "4.1.4", + "resolved": "https://registry.npmjs.org/rc-config-loader/-/rc-config-loader-4.1.4.tgz", + "integrity": "sha512-3GiwEzklkbXTDp52UR5nT8iXgYAx1V9ZG/kDZT7p60u2GCv2XTwQq4NzinMoMpNtXhmt3WkhYXcj6HH8HdwCEQ==", + "dev": true, + "dependencies": { + "debug": "^4.4.3", + "js-yaml": "^4.1.1", + "json5": "^2.2.3", + "require-from-string": "^2.0.2" + } + }, + "node_modules/read": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", + "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", + "dev": true, + "dependencies": { + "mute-stream": "~0.0.4" + }, + "engines": { + "node": ">=0.8" + } + }, + "node_modules/read-pkg": { + "version": "9.0.1", + "resolved": "https://registry.npmjs.org/read-pkg/-/read-pkg-9.0.1.tgz", + "integrity": "sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==", + "dev": true, + "dependencies": { + "@types/normalize-package-data": "^2.4.3", + "normalize-package-data": "^6.0.0", + "parse-json": "^8.0.0", + "type-fest": "^4.6.0", + "unicorn-magic": "^0.1.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/read-pkg/node_modules/unicorn-magic": { + "version": "0.1.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.1.0.tgz", + "integrity": "sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/readable-stream": { + "version": "3.6.2", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-3.6.2.tgz", + "integrity": "sha512-9u/sniCrY3D5WdsERHzHE4G2YCXqoG5FTHUiCC4SIbr6XcLZBY05ya9EKjYek9O5xOAwjGq+1JdGBAS7Q9ScoA==", + "dev": true, + "optional": true, + "dependencies": { + "inherits": "^2.0.3", + "string_decoder": "^1.1.1", + "util-deprecate": "^1.0.1" + }, + "engines": { + "node": ">= 6" + } + }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "dev": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/reusify": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", + "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", + "dev": true, + "engines": { + "iojs": ">=1.0.0", + "node": ">=0.10.0" + } + }, + "node_modules/run-applescript": { + "version": "7.1.0", + "resolved": "https://registry.npmjs.org/run-applescript/-/run-applescript-7.1.0.tgz", + "integrity": "sha512-DPe5pVFaAsinSaV6QjQ6gdiedWDcRCbUuiQfQa2wmWV7+xC9bGulGI8+TdRmoFkAPaBXk8CrAbnlY2ISniJ47Q==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/run-parallel": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.2.0.tgz", + "integrity": "sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "queue-microtask": "^1.2.2" + } + }, + "node_modules/safe-buffer": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/safe-buffer/-/safe-buffer-5.2.1.tgz", + "integrity": "sha512-rp3So07KcdmmKbGvgaNxQSJr7bGVSVk5S9Eq1F+ppbRo70+YeaDxkw5Dd8NPN+GD6bjnYm2VuPuCXmpuYvmCXQ==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, + "node_modules/safer-buffer": { + "version": "2.1.2", + "resolved": "https://registry.npmjs.org/safer-buffer/-/safer-buffer-2.1.2.tgz", + "integrity": "sha512-YZo3K82SD7Riyi0E1EQPojLz7kpepnSQI9IyPbHHg1XXXevb5dJI7tpyN2ADxGcQbHG7vcyRHk0cbwqcQriUtg==", + "dev": true + }, + "node_modules/sax": { + "version": "1.6.1", + "resolved": "https://registry.npmjs.org/sax/-/sax-1.6.1.tgz", + "integrity": "sha512-42tBVwLWnaQvW5zc4HbZrTuWccECCZfBi92FDuwtqxasH+JbPB3/FOKb1m222K42R4WxuxzzMsTswfzgtSu64Q==", + "dev": true, + "engines": { + "node": ">=11.0.0" + } + }, + "node_modules/secretlint": { + "version": "10.2.2", + "resolved": "https://registry.npmjs.org/secretlint/-/secretlint-10.2.2.tgz", + "integrity": "sha512-xVpkeHV/aoWe4vP4TansF622nBEImzCY73y/0042DuJ29iKIaqgoJ8fGxre3rVSHHbxar4FdJobmTnLp9AU0eg==", + "dev": true, + "dependencies": { + "@secretlint/config-creator": "^10.2.2", + "@secretlint/formatter": "^10.2.2", + "@secretlint/node": "^10.2.2", + "@secretlint/profiler": "^10.2.2", + "debug": "^4.4.1", + "globby": "^14.1.0", + "read-pkg": "^9.0.1" + }, + "bin": { + "secretlint": "bin/secretlint.js" + }, + "engines": { + "node": ">=20.0.0" + } + }, + "node_modules/semver": { + "version": "7.8.5", + "resolved": "https://registry.npmjs.org/semver/-/semver-7.8.5.tgz", + "integrity": "sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==", + "dev": true, + "bin": { + "semver": "bin/semver.js" + }, + "engines": { + "node": ">=10" + } + }, + "node_modules/side-channel": { + "version": "1.1.1", + "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.1.tgz", + "integrity": "sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4", + "side-channel-list": "^1.0.1", + "side-channel-map": "^1.0.1", + "side-channel-weakmap": "^1.0.2" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-list": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.1.tgz", + "integrity": "sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==", + "dev": true, + "dependencies": { + "es-errors": "^1.3.0", + "object-inspect": "^1.13.4" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-map": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/side-channel-map/-/side-channel-map-1.0.1.tgz", + "integrity": "sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/side-channel-weakmap": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/side-channel-weakmap/-/side-channel-weakmap-1.0.2.tgz", + "integrity": "sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==", + "dev": true, + "dependencies": { + "call-bound": "^1.0.2", + "es-errors": "^1.3.0", + "get-intrinsic": "^1.2.5", + "object-inspect": "^1.13.3", + "side-channel-map": "^1.0.1" + }, + "engines": { + "node": ">= 0.4" + }, + "funding": { + "url": "https://github.com/sponsors/ljharb" + } + }, + "node_modules/simple-concat": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/simple-concat/-/simple-concat-1.0.1.tgz", + "integrity": "sha512-cSFtAPtRhljv69IK0hTVZQ+OfE9nePi/rtJmw5UjHeVyVroEqJXP1sFztKUy1qU+xvz3u/sfYJLa947b7nAN2Q==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true + }, + "node_modules/simple-get": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/simple-get/-/simple-get-4.0.1.tgz", + "integrity": "sha512-brv7p5WgH0jmQJr1ZDDfKDOSeWWg+OVypG99A/5vYGPqJ6pxiaHLy8nxtFjBA7oMa01ebA9gfh1uMCFqOuXxvA==", + "dev": true, + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "optional": true, + "dependencies": { + "decompress-response": "^6.0.0", + "once": "^1.3.1", + "simple-concat": "^1.0.0" + } + }, + "node_modules/slash": { + "version": "5.1.0", + "resolved": "https://registry.npmjs.org/slash/-/slash-5.1.0.tgz", + "integrity": "sha512-ZA6oR3T/pEyuqwMgAKT0/hAv8oAXckzbkmR0UkUosQ+Mc4RxGoJkRmwHgHufaenlyAgE1Mxgpdcrf75y6XcnDg==", + "dev": true, + "engines": { + "node": ">=14.16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/slice-ansi": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/slice-ansi/-/slice-ansi-4.0.0.tgz", + "integrity": "sha512-qMCMfhY040cVHT43K9BFygqYbUPFZKHOg7K73mtTWJRb8pyP3fzf4Ixd5SzdEJQ6MRUg/WBnOLxghZtKKurENQ==", + "dev": true, + "dependencies": { + "ansi-styles": "^4.0.0", + "astral-regex": "^2.0.0", + "is-fullwidth-code-point": "^3.0.0" + }, + "engines": { + "node": ">=10" + }, + "funding": { + "url": "https://github.com/chalk/slice-ansi?sponsor=1" + } + }, + "node_modules/spdx-correct": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.2.0.tgz", + "integrity": "sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==", + "dev": true, + "dependencies": { + "spdx-expression-parse": "^3.0.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-exceptions": { + "version": "2.5.0", + "resolved": "https://registry.npmjs.org/spdx-exceptions/-/spdx-exceptions-2.5.0.tgz", + "integrity": "sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==", + "dev": true + }, + "node_modules/spdx-expression-parse": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/spdx-expression-parse/-/spdx-expression-parse-3.0.1.tgz", + "integrity": "sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==", + "dev": true, + "dependencies": { + "spdx-exceptions": "^2.1.0", + "spdx-license-ids": "^3.0.0" + } + }, + "node_modules/spdx-license-ids": { + "version": "3.0.24", + "resolved": "https://registry.npmjs.org/spdx-license-ids/-/spdx-license-ids-3.0.24.tgz", + "integrity": "sha512-cLS9TtWkIQFyLkJ3/5aFQAOHOSKTlOs/7WDut/XPSdjom1fJhUdkepeJAKXa9Y05+FabHd0sti+Et559vDtkpQ==", + "dev": true + }, + "node_modules/string_decoder": { + "version": "1.3.0", + "resolved": "https://registry.npmjs.org/string_decoder/-/string_decoder-1.3.0.tgz", + "integrity": "sha512-hkRX8U1WjJFd8LsDJ2yQ/wWWxaopEsABU1XfkM8A+j0+85JAGppt16cr1Whg6KIbb4okU6Mql6BOj+uup/wKeA==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "~5.2.0" + } + }, + "node_modules/string-width": { + "version": "4.2.3", + "resolved": "https://registry.npmjs.org/string-width/-/string-width-4.2.3.tgz", + "integrity": "sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==", + "dev": true, + "dependencies": { + "emoji-regex": "^8.0.0", + "is-fullwidth-code-point": "^3.0.0", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/string-width/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/strip-ansi": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-7.2.0.tgz", + "integrity": "sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==", + "dev": true, + "dependencies": { + "ansi-regex": "^6.2.2" + }, + "engines": { + "node": ">=12" + }, + "funding": { + "url": "https://github.com/chalk/strip-ansi?sponsor=1" + } + }, + "node_modules/strip-json-comments": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-2.0.1.tgz", + "integrity": "sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==", + "dev": true, + "optional": true, + "engines": { + "node": ">=0.10.0" + } + }, + "node_modules/structured-source": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/structured-source/-/structured-source-4.0.0.tgz", + "integrity": "sha512-qGzRFNJDjFieQkl/sVOI2dUjHKRyL9dAJi2gCPGJLbJHBIkyOHxjuocpIEfbLioX+qSJpvbYdT49/YCdMznKxA==", + "dev": true, + "dependencies": { + "boundary": "^2.0.0" + } + }, + "node_modules/supports-color": { + "version": "7.2.0", + "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", + "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/supports-hyperlinks": { + "version": "3.2.0", + "resolved": "https://registry.npmjs.org/supports-hyperlinks/-/supports-hyperlinks-3.2.0.tgz", + "integrity": "sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==", + "dev": true, + "dependencies": { + "has-flag": "^4.0.0", + "supports-color": "^7.0.0" + }, + "engines": { + "node": ">=14.18" + }, + "funding": { + "url": "https://github.com/chalk/supports-hyperlinks?sponsor=1" + } + }, + "node_modules/table": { + "version": "6.9.0", + "resolved": "https://registry.npmjs.org/table/-/table-6.9.0.tgz", + "integrity": "sha512-9kY+CygyYM6j02t5YFHbNz2FN5QmYGv9zAjVp4lCDjlCw7amdckXlEt/bjMhUIfj4ThGRE4gCUH5+yGnNuPo5A==", + "dev": true, + "dependencies": { + "ajv": "^8.0.1", + "lodash.truncate": "^4.4.2", + "slice-ansi": "^4.0.0", + "string-width": "^4.2.3", + "strip-ansi": "^6.0.1" + }, + "engines": { + "node": ">=10.0.0" + } + }, + "node_modules/table/node_modules/ansi-regex": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/ansi-regex/-/ansi-regex-5.0.1.tgz", + "integrity": "sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==", + "dev": true, + "engines": { + "node": ">=8" + } + }, + "node_modules/table/node_modules/strip-ansi": { + "version": "6.0.1", + "resolved": "https://registry.npmjs.org/strip-ansi/-/strip-ansi-6.0.1.tgz", + "integrity": "sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==", + "dev": true, + "dependencies": { + "ansi-regex": "^5.0.1" + }, + "engines": { + "node": ">=8" + } + }, + "node_modules/tar-fs": { + "version": "2.1.5", + "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.5.tgz", + "integrity": "sha512-OboTd8mmMhZDNPV+UjQcK9yKAatXu2aJ+r1w4im1Otd4M4fl2hwvdoXUxIYHFTHWK/3y3FarBP70v3vwmGlOxw==", + "dev": true, + "optional": true, + "dependencies": { + "chownr": "^1.1.1", + "mkdirp-classic": "^0.5.2", + "pump": "^3.0.0", + "tar-stream": "^2.1.4" + } + }, + "node_modules/tar-stream": { + "version": "2.2.0", + "resolved": "https://registry.npmjs.org/tar-stream/-/tar-stream-2.2.0.tgz", + "integrity": "sha512-ujeqbceABgwMZxEJnk2HDY2DlnUZ+9oEcb1KzTVfYHio0UE6dG71n60d8D2I4qNvleWrrXpmjpt7vZeF1LnMZQ==", + "dev": true, + "optional": true, + "dependencies": { + "bl": "^4.0.3", + "end-of-stream": "^1.4.1", + "fs-constants": "^1.0.0", + "inherits": "^2.0.3", + "readable-stream": "^3.1.1" + }, + "engines": { + "node": ">=6" + } + }, + "node_modules/terminal-link": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/terminal-link/-/terminal-link-4.0.0.tgz", + "integrity": "sha512-lk+vH+MccxNqgVqSnkMVKx4VLJfnLjDBGzH16JVZjKE2DoxP57s6/vt6JmXV5I3jBcfGrxNrYtC+mPtU7WJztA==", + "dev": true, + "dependencies": { + "ansi-escapes": "^7.0.0", + "supports-hyperlinks": "^3.2.0" + }, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/text-table": { + "version": "0.2.0", + "resolved": "https://registry.npmjs.org/text-table/-/text-table-0.2.0.tgz", + "integrity": "sha512-N+8UisAXDGk8PFXP4HAzVR9nbfmVJ3zYLAWiTIoqC5v5isinhr+r5uaO8+7r3BMfuNIufIsA7RdpVgacC2cSpw==", + "dev": true + }, + "node_modules/textextensions": { + "version": "6.11.0", + "resolved": "https://registry.npmjs.org/textextensions/-/textextensions-6.11.0.tgz", + "integrity": "sha512-tXJwSr9355kFJI3lbCkPpUH5cP8/M0GGy2xLO34aZCjMXBaK3SoPnZwr/oWmo1FdCnELcs4npdCIOFtq9W3ruQ==", + "dev": true, + "dependencies": { + "editions": "^6.21.0" + }, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/tmp": { + "version": "0.2.7", + "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.7.tgz", + "integrity": "sha512-e0votIpp4Uo2AJYSzVHV6xCcawuiez3DzqDAbrTc3YxBkplN6e+dM13ZeIcZnDg/QpSuU2zfZ3rzwY8ukEnaXw==", + "dev": true, + "engines": { + "node": ">=14.14" + } + }, + "node_modules/to-regex-range": { + "version": "5.0.1", + "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", + "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", + "dev": true, + "dependencies": { + "is-number": "^7.0.0" + }, + "engines": { + "node": ">=8.0" + } + }, + "node_modules/tslib": { + "version": "2.8.1", + "resolved": "https://registry.npmjs.org/tslib/-/tslib-2.8.1.tgz", + "integrity": "sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==", + "dev": true + }, + "node_modules/tunnel": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", + "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", + "dev": true, + "engines": { + "node": ">=0.6.11 <=0.7.0 || >=0.7.3" + } + }, + "node_modules/tunnel-agent": { + "version": "0.6.0", + "resolved": "https://registry.npmjs.org/tunnel-agent/-/tunnel-agent-0.6.0.tgz", + "integrity": "sha512-McnNiV1l8RYeY8tBgEpuodCC1mLUdbSN+CYBL7kJsJNInOP8UjDDEwdk6Mw60vdLLrr5NHKZhMAOSrR2NZuQ+w==", + "dev": true, + "optional": true, + "dependencies": { + "safe-buffer": "^5.0.1" + }, + "engines": { + "node": "*" + } + }, + "node_modules/type-fest": { + "version": "4.41.0", + "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-4.41.0.tgz", + "integrity": "sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==", + "dev": true, + "engines": { + "node": ">=16" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/typed-rest-client": { + "version": "1.8.11", + "resolved": "https://registry.npmjs.org/typed-rest-client/-/typed-rest-client-1.8.11.tgz", + "integrity": "sha512-5UvfMpd1oelmUPRbbaVnq+rHP7ng2cE4qoQkQeAqxRL6PklkxsM0g32/HL0yfvruK6ojQ5x8EE+HF4YV6DtuCA==", + "dev": true, + "dependencies": { + "qs": "^6.9.1", + "tunnel": "0.0.6", + "underscore": "^1.12.1" + } + }, + "node_modules/typescript": { + "version": "5.9.3", + "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.9.3.tgz", + "integrity": "sha512-jl1vZzPDinLr9eUt3J/t7V6FgNEw9QjvBPdysz9KfQDD41fQrC2Y4vKQdiaUpFT4bXlb1RHhLpp8wtm6M5TgSw==", + "dev": true, + "bin": { + "tsc": "bin/tsc", + "tsserver": "bin/tsserver" + }, + "engines": { + "node": ">=14.17" + } + }, + "node_modules/uc.micro": { + "version": "2.1.0", + "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-2.1.0.tgz", + "integrity": "sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==", + "dev": true + }, + "node_modules/underscore": { + "version": "1.13.8", + "resolved": "https://registry.npmjs.org/underscore/-/underscore-1.13.8.tgz", + "integrity": "sha512-DXtD3ZtEQzc7M8m4cXotyHR+FAS18C64asBYY5vqZexfYryNNnDc02W4hKg3rdQuqOYas1jkseX0+nZXjTXnvQ==", + "dev": true + }, + "node_modules/undici": { + "version": "7.30.1", + "resolved": "https://registry.npmjs.org/undici/-/undici-7.30.1.tgz", + "integrity": "sha512-03KrMbjIFDWQR5RNPrSw3bd1r12JpyFQl9/yxjUbJtqkjmllqC5lFjZh6ujeuJCKr8EOQvRW872Oy/az+nLYxg==", + "dev": true, + "engines": { + "node": ">=20.18.1" + } + }, + "node_modules/undici-types": { + "version": "6.21.0", + "resolved": "https://registry.npmjs.org/undici-types/-/undici-types-6.21.0.tgz", + "integrity": "sha512-iwDZqg0QAGrg9Rav5H4n0M64c3mkR59cJ6wQp+7C4nI0gsmExaedaYLNO44eT4AtBBwjbTiGPMlt2Md0T9H9JQ==", + "dev": true + }, + "node_modules/unicorn-magic": { + "version": "0.3.0", + "resolved": "https://registry.npmjs.org/unicorn-magic/-/unicorn-magic-0.3.0.tgz", + "integrity": "sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==", + "dev": true, + "engines": { + "node": ">=18" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/universalify": { + "version": "2.0.1", + "resolved": "https://registry.npmjs.org/universalify/-/universalify-2.0.1.tgz", + "integrity": "sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==", + "dev": true, + "engines": { + "node": ">= 10.0.0" + } + }, + "node_modules/url-join": { + "version": "4.0.1", + "resolved": "https://registry.npmjs.org/url-join/-/url-join-4.0.1.tgz", + "integrity": "sha512-jk1+QP6ZJqyOiuEI9AEWQfju/nB2Pw466kbA0LEZljHwKeMgd9WrAEgEGxjPDD2+TNbbb37rTyhEfrCXfuKXnA==", + "dev": true + }, + "node_modules/util-deprecate": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/util-deprecate/-/util-deprecate-1.0.2.tgz", + "integrity": "sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==", + "dev": true, + "optional": true + }, + "node_modules/validate-npm-package-license": { + "version": "3.0.4", + "resolved": "https://registry.npmjs.org/validate-npm-package-license/-/validate-npm-package-license-3.0.4.tgz", + "integrity": "sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==", + "dev": true, + "dependencies": { + "spdx-correct": "^3.0.0", + "spdx-expression-parse": "^3.0.0" + } + }, + "node_modules/version-range": { + "version": "4.15.0", + "resolved": "https://registry.npmjs.org/version-range/-/version-range-4.15.0.tgz", + "integrity": "sha512-Ck0EJbAGxHwprkzFO966t4/5QkRuzh+/I1RxhLgUKKwEn+Cd8NwM60mE3AqBZg5gYODoXW0EFsQvbZjRlvdqbg==", + "dev": true, + "engines": { + "node": ">=4" + }, + "funding": { + "url": "https://bevry.me/fund" + } + }, + "node_modules/whatwg-encoding": { + "version": "3.1.1", + "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-3.1.1.tgz", + "integrity": "sha512-6qN4hJdMwfYBtE3YBTTHhoeuUrDBPZmbQaxWAqSALV/MeEnR5z1xd8UKud2RAkFoPkmB+hli1TZSnyi84xz1vQ==", + "deprecated": "Use @exodus/bytes instead for a more spec-conformant and faster implementation", + "dev": true, + "dependencies": { + "iconv-lite": "0.6.3" + }, + "engines": { + "node": ">=18" + } + }, + "node_modules/whatwg-mimetype": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/whatwg-mimetype/-/whatwg-mimetype-4.0.0.tgz", + "integrity": "sha512-QaKxh0eNIi2mE9p2vEdzfagOKHCcj1pJ56EEHGQOVxp8r9/iszLUUV7v89x9O1p/T+NlTM5W7jW6+cz4Fq1YVg==", + "dev": true, + "engines": { + "node": ">=18" + } + }, + "node_modules/wrappy": { + "version": "1.0.2", + "resolved": "https://registry.npmjs.org/wrappy/-/wrappy-1.0.2.tgz", + "integrity": "sha512-l4Sp/DRseor9wL6EvV2+TuQn63dMkPjZ/sp9XkghTEbV9KlPS1xUsZ3u7/IQO4wxtcFB4bgpQPRcR3QCvezPcQ==", + "dev": true, + "optional": true + }, + "node_modules/wsl-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-1.0.1.tgz", + "integrity": "sha512-c16PKLIRxBYpR3jSWsdS7SFp0COm5SOQdiFjY818dYFHJbMGuLBszpF07LBWmOS1uzrHa9VGdynsEgoeKy0oOQ==", + "dev": true, + "dependencies": { + "is-wsl": "^3.1.0", + "powershell-utils": "^0.2.0" + }, + "engines": { + "node": ">=20" + }, + "funding": { + "url": "https://github.com/sponsors/sindresorhus" + } + }, + "node_modules/xml2js": { + "version": "0.5.0", + "resolved": "https://registry.npmjs.org/xml2js/-/xml2js-0.5.0.tgz", + "integrity": "sha512-drPFnkQJik/O+uPKpqSgr22mpuFHqKdbS835iAQrUC73L2F5WkboIRd63ai/2Yg6I1jzifPFKH2NTK+cfglkIA==", + "dev": true, + "dependencies": { + "sax": ">=0.6.0", + "xmlbuilder": "~11.0.0" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/xmlbuilder": { + "version": "11.0.1", + "resolved": "https://registry.npmjs.org/xmlbuilder/-/xmlbuilder-11.0.1.tgz", + "integrity": "sha512-fDlsI/kFEx7gLvbecc0/ohLG50fugQp8ryHzMTuW9vSa1GJ0XYWKnhsUx7oie3G98+r56aTQIUB4kht42R3JvA==", + "dev": true, + "engines": { + "node": ">=4.0" + } + }, + "node_modules/yallist": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/yallist/-/yallist-4.0.0.tgz", + "integrity": "sha512-3wdGidZyq5PB084XLES5TpOSRA3wjXAlIWMhum2kRcv/41Sn2emQ0dycQW4uZXLejwKvg6EsvbdlVL+FYEct7A==", + "dev": true + }, + "node_modules/yauzl": { + "version": "3.4.0", + "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-3.4.0.tgz", + "integrity": "sha512-jIH9yLR9wqr0wOS0TpBvo/g/2UgZH5qePVbjgRliiF0BYvOZyaBknKsF+x9Iht0O6sqgnB93rCICdOZFecJuDw==", + "dev": true, + "dependencies": { + "pend": "~1.2.0" + }, + "engines": { + "node": ">=12" + } + }, + "node_modules/yazl": { + "version": "2.5.1", + "resolved": "https://registry.npmjs.org/yazl/-/yazl-2.5.1.tgz", + "integrity": "sha512-phENi2PLiHnHb6QBVot+dJnaAZ0xosj7p3fWl+znIjBDlnMI2PsZCJZ306BPTFOaHf5qdDEI8x5qFrSOBN5vrw==", + "dev": true, + "dependencies": { + "buffer-crc32": "~0.2.3" + } + } + } +} diff --git a/package.json b/package.json new file mode 100644 index 0000000..c18b292 --- /dev/null +++ b/package.json @@ -0,0 +1,1022 @@ +{ + "name": "vs-code-cmake-helper", + "displayName": "VS Code CMake Helper", + "description": "CMake the way CLion does it: profiles and presets, targets with their sources, one active target to build, run and debug, run configurations per target, CTest in the Test Explorer.", + "version": "0.1.0", + "publisher": "cernik", + "license": "UNLICENSED", + "engines": { + "vscode": "^1.85.0" + }, + "categories": [ + "Programming Languages", + "Testing", + "Debuggers", + "Other" + ], + "keywords": [ + "cmake", + "ctest", + "c++", + "clion", + "targets" + ], + "main": "./out/extension.js", + "activationEvents": [ + "workspaceContains:CMakeLists.txt", + "workspaceContains:CMakePresets.json", + "workspaceContains:*/CMakeLists.txt" + ], + "scripts": { + "compile": "tsc -p .", + "watch": "tsc -watch -p .", + "typecheck": "tsc -p . --noEmit", + "package": "npm run compile && vsce package --no-dependencies" + }, + "devDependencies": { + "@types/node": "^20.0.0", + "@types/vscode": "~1.85.0", + "@vscode/vsce": "^3.9.2", + "typescript": "^5.6.0" + }, + "contributes": { + "viewsContainers": { + "activitybar": [ + { + "id": "cmakeHelper", + "title": "CMake", + "icon": "media/cmake.svg" + } + ] + }, + "views": { + "cmakeHelper": [ + { + "id": "cmakeHelper.targets", + "name": "Targets", + "icon": "media/cmake.svg" + }, + { + "id": "cmakeHelper.profiles", + "name": "Profiles", + "icon": "media/cmake.svg", + "when": "cmakeHelper.hasProject" + } + ] + }, + "viewsWelcome": [ + { + "view": "cmakeHelper.targets", + "contents": "There's no CMakeLists.txt in this workspace.\nIf the project is in a subfolder, set its path.\n[Set Source Directory](command:workbench.action.openSettings?%5B%22cmakeHelper.sourceDirectory%22%5D)", + "when": "!cmakeHelper.hasProject" + }, + { + "view": "cmakeHelper.targets", + "contents": "The CMake project isn't loaded yet.\n[Load CMake Project](command:cmakeHelper.reload)\n[Show CMake Output](command:cmakeHelper.showOutput)", + "when": "cmakeHelper.hasProject && !cmakeHelper.loaded && !cmakeHelper.busy" + } + ], + "commands": [ + { + "command": "cmakeHelper.reload", + "title": "Reload CMake Project", + "category": "CMake", + "icon": "$(refresh)" + }, + { + "command": "cmakeHelper.loadProject", + "title": "Load CMake Project", + "category": "CMake", + "icon": "$(folder-opened)" + }, + { + "command": "cmakeHelper.resetCacheAndReload", + "title": "Reset Cache and Reload Project", + "category": "CMake", + "icon": "$(clear-all)" + }, + { + "command": "cmakeHelper.cancelReload", + "title": "Stop Loading the CMake Project", + "category": "CMake", + "icon": "$(debug-stop)" + }, + { + "command": "cmakeHelper.showOutput", + "title": "Show CMake Output", + "category": "CMake", + "icon": "$(output)" + }, + { + "command": "cmakeHelper.checkForUpdates", + "title": "Check for CMake Helper Updates", + "category": "CMake" + }, + { + "command": "cmakeHelper.setUpdateToken", + "title": "Set Gitea Token for CMake Helper Updates", + "category": "CMake" + }, + { + "command": "cmakeHelper.patchTitleBar", + "title": "Put the CMake Toolbar in the Title Bar (Patch VS Code)", + "category": "CMake" + }, + { + "command": "cmakeHelper.unpatchTitleBar", + "title": "Remove the CMake Toolbar from the Title Bar (Unpatch VS Code)", + "category": "CMake" + }, + { + "command": "cmakeHelper.selectProfile", + "title": "Select Profile…", + "category": "CMake", + "icon": "$(layers)" + }, + { + "command": "cmakeHelper.newProfile", + "title": "New Profile…", + "category": "CMake", + "icon": "$(add)" + }, + { + "command": "cmakeHelper.editProfiles", + "title": "Edit Profiles…", + "category": "CMake", + "icon": "$(settings-gear)" + }, + { + "command": "cmakeHelper.editProfilesJson", + "title": "Edit Profiles (settings.json)", + "category": "CMake", + "icon": "$(json)" + }, + { + "command": "cmakeHelper.removeProfile", + "title": "Remove Profile", + "category": "CMake", + "icon": "$(trash)" + }, + { + "command": "cmakeHelper.revealBuildDirectory", + "title": "Open Build Directory", + "category": "CMake", + "icon": "$(folder-opened)" + }, + { + "command": "cmakeHelper.selectTarget", + "title": "Select Target…", + "category": "CMake", + "icon": "$(target)" + }, + { + "command": "cmakeHelper.build", + "title": "Build", + "category": "CMake", + "icon": "$(tools)" + }, + { + "command": "cmakeHelper.rebuild", + "title": "Rebuild", + "category": "CMake", + "icon": "$(refresh)" + }, + { + "command": "cmakeHelper.buildAll", + "title": "Build All", + "category": "CMake", + "icon": "$(tools)" + }, + { + "command": "cmakeHelper.rebuildAll", + "title": "Rebuild All", + "category": "CMake" + }, + { + "command": "cmakeHelper.clean", + "title": "Clean", + "category": "CMake", + "icon": "$(trash)" + }, + { + "command": "cmakeHelper.run", + "title": "Run", + "category": "CMake", + "icon": "$(play)" + }, + { + "command": "cmakeHelper.debug", + "title": "Debug", + "category": "CMake", + "icon": "$(debug-alt)" + }, + { + "command": "cmakeHelper.editRunConfiguration", + "title": "Edit Run Configuration…", + "category": "CMake", + "icon": "$(settings-gear)" + }, + { + "command": "cmakeHelper.editGdbConfigurations", + "title": "Edit GDB Server Configurations…", + "category": "CMake", + "icon": "$(debug-configure)" + }, + { + "command": "cmakeHelper.newGdbConfiguration", + "title": "New GDB Server Configuration (Embedded / Remote)…", + "category": "CMake", + "icon": "$(add)" + }, + { + "command": "cmakeHelper.revealDefinition", + "title": "Go to Definition in CMakeLists", + "category": "CMake", + "icon": "$(go-to-file)" + }, + { + "command": "cmakeHelper.copyArtifactPath", + "title": "Copy Path of the Built File", + "category": "CMake" + }, + { + "command": "cmakeHelper.activeProgram", + "title": "Active Target's Program (for launch.json)", + "category": "CMake" + }, + { + "command": "cmakeHelper.activeTarget", + "title": "Active Target's Name", + "category": "CMake" + }, + { + "command": "cmakeHelper.buildDirectory", + "title": "Active Build Directory", + "category": "CMake" + }, + { + "command": "cmakeHelper.buildType", + "title": "Active Build Type", + "category": "CMake" + } + ], + "menus": { + "explorer/context": [ + { + "command": "cmakeHelper.loadProject", + "when": "resourceFilename == CMakeLists.txt && !(resourcePath in cmakeHelper.loadedCMakeLists)", + "group": "cmake@1" + }, + { + "command": "cmakeHelper.reload", + "when": "resourceFilename == CMakeLists.txt && resourcePath in cmakeHelper.loadedCMakeLists", + "group": "cmake@1" + } + ], + "editor/context": [ + { + "command": "cmakeHelper.loadProject", + "when": "resourceFilename == CMakeLists.txt && !(resourcePath in cmakeHelper.loadedCMakeLists)", + "group": "cmake@1" + }, + { + "command": "cmakeHelper.reload", + "when": "resourceFilename == CMakeLists.txt && resourcePath in cmakeHelper.loadedCMakeLists", + "group": "cmake@1" + } + ], + "editor/title/context": [ + { + "command": "cmakeHelper.loadProject", + "when": "resourceFilename == CMakeLists.txt && !(resourcePath in cmakeHelper.loadedCMakeLists)", + "group": "cmake@1" + }, + { + "command": "cmakeHelper.reload", + "when": "resourceFilename == CMakeLists.txt && resourcePath in cmakeHelper.loadedCMakeLists", + "group": "cmake@1" + } + ], + "view/title": [ + { + "command": "cmakeHelper.reload", + "when": "view == cmakeHelper.targets && !cmakeHelper.busy", + "group": "navigation@1" + }, + { + "command": "cmakeHelper.cancelReload", + "when": "view == cmakeHelper.targets && cmakeHelper.busy", + "group": "navigation@1" + }, + { + "command": "cmakeHelper.selectProfile", + "when": "view == cmakeHelper.targets", + "group": "navigation@2" + }, + { + "command": "cmakeHelper.buildAll", + "when": "view == cmakeHelper.targets && cmakeHelper.loaded", + "group": "navigation@3" + }, + { + "command": "cmakeHelper.rebuildAll", + "when": "view == cmakeHelper.targets && cmakeHelper.loaded", + "group": "1_build@1" + }, + { + "command": "cmakeHelper.clean", + "when": "view == cmakeHelper.targets && cmakeHelper.loaded", + "group": "1_build@2" + }, + { + "command": "cmakeHelper.resetCacheAndReload", + "when": "view == cmakeHelper.targets", + "group": "2_cmake@1" + }, + { + "command": "cmakeHelper.showOutput", + "when": "view == cmakeHelper.targets", + "group": "2_cmake@2" + }, + { + "command": "cmakeHelper.newProfile", + "when": "view == cmakeHelper.profiles", + "group": "navigation@1" + }, + { + "command": "cmakeHelper.editProfiles", + "when": "view == cmakeHelper.profiles", + "group": "navigation@2" + }, + { + "command": "cmakeHelper.editProfiles", + "when": "view == cmakeHelper.targets", + "group": "2_cmake@0" + }, + { + "command": "cmakeHelper.editProfilesJson", + "when": "view == cmakeHelper.profiles", + "group": "1_edit@1" + }, + { + "command": "cmakeHelper.editGdbConfigurations", + "when": "view == cmakeHelper.targets", + "group": "2_cmake@3" + } + ], + "view/item/context": [ + { + "command": "cmakeHelper.editProfiles", + "when": "view == cmakeHelper.profiles", + "group": "inline@0" + }, + { + "command": "cmakeHelper.run", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\.(executable|utility)|\\.run$/", + "group": "inline@1" + }, + { + "command": "cmakeHelper.debug", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\.executable|\\.run$/", + "group": "inline@2" + }, + { + "command": "cmakeHelper.build", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "inline@3" + }, + { + "command": "cmakeHelper.selectTarget", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "1_active@1" + }, + { + "command": "cmakeHelper.run", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\.(executable|utility)|\\.run$/", + "group": "2_run@1" + }, + { + "command": "cmakeHelper.debug", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\.executable|\\.run$/", + "group": "2_run@2" + }, + { + "command": "cmakeHelper.editRunConfiguration", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "2_run@3" + }, + { + "command": "cmakeHelper.build", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "3_build@1" + }, + { + "command": "cmakeHelper.rebuild", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "3_build@2" + }, + { + "command": "cmakeHelper.revealDefinition", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\./", + "group": "4_nav@1" + }, + { + "command": "cmakeHelper.copyArtifactPath", + "when": "view == cmakeHelper.targets && viewItem =~ /^target\\.(executable|library)/", + "group": "4_nav@2" + }, + { + "command": "cmakeHelper.revealBuildDirectory", + "when": "view == cmakeHelper.profiles", + "group": "inline@1" + }, + { + "command": "cmakeHelper.resetCacheAndReload", + "when": "view == cmakeHelper.profiles", + "group": "1_cmake@1" + }, + { + "command": "cmakeHelper.revealBuildDirectory", + "when": "view == cmakeHelper.profiles", + "group": "1_cmake@2" + }, + { + "command": "cmakeHelper.removeProfile", + "when": "view == cmakeHelper.profiles && viewItem == profile.settings", + "group": "2_edit@1" + }, + { + "command": "cmakeHelper.editProfiles", + "when": "view == cmakeHelper.profiles", + "group": "2_edit@0" + } + ], + "editor/title/run": [ + { + "command": "cmakeHelper.run", + "when": "cmakeHelper.loaded && resourceLangId =~ /^(c|cpp|cuda-cpp)$/ && config.cmakeHelper.toolbar != editorTitle", + "group": "navigation@1" + }, + { + "command": "cmakeHelper.debug", + "when": "cmakeHelper.loaded && resourceLangId =~ /^(c|cpp|cuda-cpp)$/ && config.cmakeHelper.toolbar != editorTitle", + "group": "navigation@2" + } + ], + "commandPalette": [ + { + "command": "cmakeHelper.loadProject", + "when": "resourceFilename == CMakeLists.txt" + }, + { + "command": "cmakeHelper.removeProfile", + "when": "false" + }, + { + "command": "cmakeHelper.revealDefinition", + "when": "cmakeHelper.loaded" + }, + { + "command": "cmakeHelper.copyArtifactPath", + "when": "cmakeHelper.loaded" + }, + { + "command": "cmakeHelper.cancelReload", + "when": "cmakeHelper.busy" + }, + { + "command": "cmakeHelper.activeProgram", + "when": "false" + }, + { + "command": "cmakeHelper.activeTarget", + "when": "false" + }, + { + "command": "cmakeHelper.buildDirectory", + "when": "false" + }, + { + "command": "cmakeHelper.buildType", + "when": "false" + } + ], + "editor/title": [ + { + "command": "cmakeHelper.reload", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject && cmakeHelper.needsReload", + "group": "navigation@-10" + }, + { + "command": "cmakeHelper.selectProfile", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject", + "group": "navigation@-9" + }, + { + "command": "cmakeHelper.selectTarget", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject && cmakeHelper.loaded", + "group": "navigation@-8" + }, + { + "command": "cmakeHelper.build", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject && cmakeHelper.loaded", + "group": "navigation@-7" + }, + { + "command": "cmakeHelper.run", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject && cmakeHelper.loaded", + "group": "navigation@-6" + }, + { + "command": "cmakeHelper.debug", + "when": "config.cmakeHelper.toolbar == editorTitle && cmakeHelper.hasProject && cmakeHelper.loaded", + "group": "navigation@-5" + } + ] + }, + "keybindings": [ + { + "command": "cmakeHelper.build", + "key": "ctrl+f9", + "mac": "cmd+f9", + "when": "cmakeHelper.hasProject" + }, + { + "command": "cmakeHelper.run", + "key": "shift+f10", + "when": "cmakeHelper.hasProject && !inDebugMode" + }, + { + "command": "cmakeHelper.debug", + "key": "shift+f9", + "when": "cmakeHelper.hasProject && !inDebugMode" + }, + { + "command": "cmakeHelper.selectTarget", + "key": "alt+shift+f10", + "when": "cmakeHelper.hasProject" + } + ], + "taskDefinitions": [ + { + "type": "cmakeHelper", + "required": [ + "kind" + ], + "properties": { + "kind": { + "type": "string", + "enum": [ + "build", + "run" + ] + }, + "name": { + "type": "string" + } + } + } + ], + "problemMatchers": [ + { + "name": "cmake-helper-gcc", + "owner": "cpp", + "source": "build", + "fileLocation": [ + "autoDetect", + "${workspaceFolder}" + ], + "pattern": { + "regexp": "^(.*?):(\\d+):(\\d*):?\\s+(?:fatal\\s+)?(warning|error|note):\\s+(.*)$", + "file": 1, + "line": 2, + "column": 3, + "severity": 4, + "message": 5 + } + } + ], + "configuration": { + "title": "CMake Helper", + "properties": { + "cmakeHelper.cmakePath": { + "type": "string", + "default": "cmake", + "description": "The cmake program.", + "scope": "machine-overridable" + }, + "cmakeHelper.ctestPath": { + "type": "string", + "default": "", + "description": "The ctest program. Empty: next to cmakePath, or ctest on PATH.", + "scope": "machine-overridable" + }, + "cmakeHelper.sourceDirectory": { + "type": "string", + "default": "${workspaceFolder}", + "description": "Where the top CMakeLists.txt is.", + "scope": "resource" + }, + "cmakeHelper.profiles": { + "type": "array", + "default": [], + "markdownDescription": "CMake profiles, as in CLion: each its own build directory. Configure presets (`CMakePresets.json`) are listed too. With neither, there's one: **Debug** in `cmake-build-debug`. Edited in **CMake: Edit Profiles…**.", + "scope": "resource", + "items": { + "type": "object", + "required": [ + "name" + ], + "properties": { + "name": { + "type": "string" + }, + "enabled": { + "type": "boolean", + "default": true, + "description": "false: kept, but not offered (CLion's \"Enable profile\")." + }, + "buildType": { + "type": "string", + "default": "Debug", + "enum": [ + "Debug", + "Release", + "RelWithDebInfo", + "MinSizeRel" + ] + }, + "buildDirectory": { + "type": "string", + "markdownDescription": "Default: `cmake-build-`. `${sourceDir}`, `${workspaceFolder}`, `${profile}`, `${buildType}` and `${env:X}` are expanded." + }, + "generator": { + "type": "string", + "examples": [ + "Ninja", + "Ninja Multi-Config", + "Unix Makefiles" + ] + }, + "toolchainFile": { + "type": "string" + }, + "cacheVariables": { + "type": "object", + "additionalProperties": { + "type": [ + "string", + "boolean" + ] + } + }, + "environment": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "For configuring, building, running and testing." + }, + "configureArgs": { + "type": "array", + "items": { + "type": "string" + } + }, + "buildArgs": { + "type": "array", + "items": { + "type": "string" + }, + "description": "After `cmake --build `; put the native tool's after `--`." + } + } + } + }, + "cmakeHelper.usePresets": { + "type": "boolean", + "default": true, + "markdownDescription": "Offer the configure presets of `CMakePresets.json` and `CMakeUserPresets.json` as profiles." + }, + "cmakeHelper.generator": { + "type": "string", + "default": "", + "description": "The generator of profiles that don't name one. Empty: Ninja if it's on PATH, else CMake's default." + }, + "cmakeHelper.configureArgs": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "More arguments for every configure." + }, + "cmakeHelper.environment": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "default": {}, + "description": "Environment of every profile." + }, + "cmakeHelper.configureOnOpen": { + "type": "boolean", + "default": true, + "description": "Load (configure) the project when the folder opens and its build directory has nothing to read yet." + }, + "cmakeHelper.autoReload": { + "type": "string", + "enum": [ + "ask", + "always", + "never" + ], + "default": "ask", + "enumDescriptions": [ + "Offer to reload when a CMake file changes", + "Reload when a CMake file changes", + "Only when asked" + ], + "description": "What a change to a CMake file does." + }, + "cmakeHelper.revealOutputOnConfigure": { + "type": "boolean", + "default": false, + "description": "Show the CMake output while loading (it's always shown when loading fails)." + }, + "cmakeHelper.exportCompileCommands": { + "type": "boolean", + "default": true, + "description": "Configure with CMAKE_EXPORT_COMPILE_COMMANDS=ON, for clangd and C/C++." + }, + "cmakeHelper.copyCompileCommands": { + "type": "string", + "default": "", + "markdownDescription": "After loading, copy `compile_commands.json` here (e.g. `${sourceDir}/compile_commands.json` for clangd). Empty: don't." + }, + "cmakeHelper.parallelJobs": { + "type": "number", + "default": 0, + "description": "Build jobs. 0: as many as there are CPUs." + }, + "cmakeHelper.revealBuildOutput": { + "type": "boolean", + "default": true, + "description": "Show the terminal with the build's output." + }, + "cmakeHelper.showUtilityTargets": { + "type": "boolean", + "default": true, + "description": "List utility targets (add_custom_target) too, as CLion does; CTest's dashboard targets (Experimental, Nightly, Continuous…) never." + }, + "cmakeHelper.buildBeforeRun": { + "type": "boolean", + "default": true, + "description": "Build a target before running or debugging it (a run configuration can say otherwise)." + }, + "cmakeHelper.defaultWorkingDirectory": { + "type": "string", + "default": "${targetDir}", + "markdownDescription": "Where a target runs unless its run configuration says. `${targetDir}` is the directory of its executable; also `${buildDir}`, `${sourceDir}`, `${workspaceFolder}`." + }, + "cmakeHelper.runConfigurations": { + "type": "object", + "default": {}, + "markdownDescription": "Run configurations by target name, as CLion's Run/Debug Configurations (Edit Run Configuration… in a target's menu, Edit Configurations… in the target picker).", + "scope": "resource", + "additionalProperties": { + "type": "object", + "properties": { + "executable": { + "type": "string", + "description": "What runs after the target is built, relative to the build directory: its own executable unless this is set (what a custom target or a library runs)." + }, + "args": { + "type": "string", + "description": "Program arguments, as on a command line." + }, + "workingDirectory": { + "type": "string" + }, + "environment": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "buildBeforeRun": { + "type": "boolean" + } + } + } + }, + "cmakeHelper.debugger": { + "type": "string", + "enum": [ + "auto", + "cppdbg", + "cppvsdbg", + "lldb", + "lldb-dap" + ], + "default": "auto", + "enumDescriptions": [ + "The first installed of C/C++, CodeLLDB, LLDB DAP", + "C/C++ (gdb, or lldb on macOS)", + "C/C++ with the Visual Studio debugger", + "CodeLLDB", + "LLDB DAP" + ], + "description": "Which debugger extension starts the debugging." + }, + "cmakeHelper.debugConfiguration": { + "type": "object", + "default": {}, + "markdownDescription": "More launch properties by debugger type, e.g. `{ \"cppdbg\": { \"miDebuggerPath\": \"/usr/bin/gdb\" } }`." + }, + "cmakeHelper.codeLens": { + "type": "boolean", + "default": true, + "description": "Run / Debug above main() and above the targets in CMake files." + }, + "cmakeHelper.toolbar": { + "type": "string", + "enum": [ + "statusBar", + "editorTitle", + "titleBar" + ], + "default": "statusBar", + "scope": "application", + "enumDescriptions": [ + "The profile, target, Build, Run and Debug in the status bar", + "Build, Run, Debug and the profile and target pickers as buttons at the top of the editors (the profile and target names stay in the status bar)", + "In VS Code's title bar, as in Jarvis's web IDE: VS Code's workbench.html is patched (with your password if VS Code is root's), and again after each VS Code update" + ], + "description": "Where CLion's toolbar (profile, target, Build, Run, Debug) goes." + }, + "cmakeHelper.buildBeforeTest": { + "type": "boolean", + "default": true, + "description": "Build the tests' targets before running them." + }, + "cmakeHelper.testParallelJobs": { + "type": "number", + "default": 0, + "description": "ctest -j. 0: as many as there are CPUs." + }, + "cmakeHelper.ctestArgs": { + "type": "array", + "items": { + "type": "string" + }, + "default": [], + "description": "More arguments for ctest." + }, + "cmakeHelper.gdbConfigurations": { + "type": "array", + "default": [], + "scope": "resource", + "markdownDescription": "GDB server configurations, as CLion's *Embedded GDB Server* and *Remote GDB Server*: chosen in the target picker, they're what Build, ▶ Run (Download & Run, or run over SSH) and 🐞 Debug act on. Edited in **CMake: Edit GDB Server Configurations…**. `${program}`, `${sourceDir}`, `${buildDir}`, `${targetDir}` and `${env:X}` are expanded.", + "items": { + "type": "object", + "required": [ + "name", + "kind" + ], + "properties": { + "name": { + "type": "string" + }, + "kind": { + "type": "string", + "enum": [ + "embedded", + "remote" + ], + "enumDescriptions": [ + "A GDB server started here (OpenOCD, J-Link, ST-LINK, pyOCD…), the program flashed", + "A gdbserver elsewhere; optionally uploaded and started over SSH" + ] + }, + "target": { + "type": "string", + "description": "The CMake target debugged (and flashed or uploaded). Empty: the active target." + }, + "program": { + "type": "string", + "description": "Another file instead of the target's executable." + }, + "debugger": { + "type": "string", + "description": "gdb for the target's architecture (gdb-multiarch, arm-none-eabi-gdb…)." + }, + "remote": { + "type": "string", + "description": "What 'target remote' gets, e.g. localhost:3333." + }, + "server": { + "type": "string", + "description": "Embedded: the GDB server program started first." + }, + "serverArgs": { + "type": "string" + }, + "serverReady": { + "type": "string", + "description": "Embedded: a regular expression the server prints once it listens." + }, + "serverEnvironment": { + "type": "object", + "additionalProperties": { + "type": "string" + } + }, + "download": { + "type": "string", + "enum": [ + "always", + "updated", + "never" + ], + "default": "always" + }, + "resetCommand": { + "type": "string", + "description": "Embedded: e.g. monitor reset halt; before and after the download." + }, + "runCommands": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Embedded, Download & Run: gdb commands that start the program after the download (default: monitor reset run)." + }, + "sysroot": { + "type": "string" + }, + "pathMappings": { + "type": "object", + "additionalProperties": { + "type": "string" + }, + "description": "Remote: build path → local path." + }, + "ssh": { + "type": "string", + "description": "Remote: user@host to upload the program to and start gdbserver on." + }, + "remotePath": { + "type": "string" + }, + "gdbserver": { + "type": "string" + }, + "args": { + "type": "string" + }, + "initCommands": { + "type": "array", + "items": { + "type": "string" + } + }, + "connectCommands": { + "type": "array", + "items": { + "type": "string" + } + }, + "buildBeforeDebug": { + "type": "boolean", + "default": true + } + } + } + }, + "cmakeHelper.updates.check": { + "type": "boolean", + "default": true, + "scope": "application", + "description": "Look for a newer CMake Helper in the Gitea releases, on start and every 10 minutes." + }, + "cmakeHelper.updates.server": { + "type": "string", + "default": "https://git.cernikovi.cz", + "scope": "application", + "description": "The Gitea server the releases are on." + }, + "cmakeHelper.updates.repository": { + "type": "string", + "default": "shin/VS-Code-CMake-helper", + "scope": "application", + "description": "The repository (owner/name) the releases are in." + } + } + } + }, + "repository": { + "type": "git", + "url": "ssh://git@git.cernikovi.cz:1022/shin/VS-Code-CMake-helper.git" + } +} diff --git a/src/codeLens.ts b/src/codeLens.ts new file mode 100644 index 0000000..89c350d --- /dev/null +++ b/src/codeLens.ts @@ -0,0 +1,62 @@ +/** + * CLion's gutter ▶, as code lenses: above main() of an executable's source, + * and above each add_executable / add_library in the CMake files. + */ +import * as vscode from "vscode"; +import type { CMakeProject } from "./project"; +import { samePath, settings } from "./util"; + +const MAIN = /^[ \t]*(?:(?:int|auto|signed|void)[ \t]+)?(?:__cdecl[ \t]+)?(?:w?main|wWinMain|WinMain)[ \t]*\(/gm; + +export class LensProvider implements vscode.CodeLensProvider { + private readonly changed = new vscode.EventEmitter(); + readonly onDidChangeCodeLenses = this.changed.event; + + constructor(private readonly project: () => CMakeProject | undefined) {} + + refresh(): void { + this.changed.fire(); + } + + provideCodeLenses(doc: vscode.TextDocument): vscode.CodeLens[] { + const p = this.project(); + if (!p?.model || !settings().get("codeLens", true)) return []; + return doc.languageId === "cmake" || /(CMakeLists\.txt|\.cmake)$/i.test(doc.fileName) ? this.cmakeLenses(p, doc) : this.mainLenses(p, doc); + } + + private mainLenses(p: CMakeProject, doc: vscode.TextDocument): vscode.CodeLens[] { + const exes = p.targetsOf(doc.fileName).filter((t) => t.type === "EXECUTABLE"); + if (!exes.length) return []; + const text = doc.getText(); + MAIN.lastIndex = 0; + const m = MAIN.exec(text); + if (!m) return []; + const range = new vscode.Range(doc.positionAt(m.index), doc.positionAt(m.index)); + const lenses: vscode.CodeLens[] = []; + for (const t of exes) { + const suffix = exes.length > 1 ? ` ${t.name}` : ""; + lenses.push( + new vscode.CodeLens(range, { title: `$(play) Run${suffix}`, command: "cmakeHelper.run", arguments: [t.name], tooltip: `Run ${t.name}` }), + new vscode.CodeLens(range, { title: `$(debug-alt) Debug${suffix}`, command: "cmakeHelper.debug", arguments: [t.name], tooltip: `Debug ${t.name}` }), + ); + } + return lenses; + } + + private cmakeLenses(p: CMakeProject, doc: vscode.TextDocument): vscode.CodeLens[] { + const lenses: vscode.CodeLens[] = []; + for (const t of p.visibleTargets()) { + const d = t.definition; + if (!d || !samePath(d.file, doc.fileName) || d.line > doc.lineCount) continue; + const range = doc.lineAt(d.line - 1).range; + if (t.type === "EXECUTABLE") { + lenses.push( + new vscode.CodeLens(range, { title: "$(play) Run", command: "cmakeHelper.run", arguments: [t.name] }), + new vscode.CodeLens(range, { title: "$(debug-alt) Debug", command: "cmakeHelper.debug", arguments: [t.name] }), + ); + } + lenses.push(new vscode.CodeLens(range, { title: "$(tools) Build", command: "cmakeHelper.build", arguments: [t.name] })); + } + return lenses; + } +} diff --git a/src/editorPage.ts b/src/editorPage.ts new file mode 100644 index 0000000..4cf129d --- /dev/null +++ b/src/editorPage.ts @@ -0,0 +1,94 @@ +/** + * The frame of the settings editors (profiles, GDB server configurations), as + * CLion's dialogs: the list on the left with add, remove, copy and move, the + * selected item's form on the right, OK / Cancel / Apply at the bottom. Each + * editor brings its own script, which fills #list and #form. An editor of a + * fixed list (the run configurations: one per target) goes without the tools. + */ +import * as vscode from "vscode"; + +const CSS = ` + body { padding: 0; margin: 0; color: var(--vscode-foreground); font: var(--vscode-font-size) var(--vscode-font-family); } + .wrap { display: flex; flex-direction: column; height: 100vh; } + .main { display: flex; flex: 1; min-height: 0; } + .side { width: 240px; border-right: 1px solid var(--vscode-panel-border); display: flex; flex-direction: column; } + .tools { display: flex; gap: 2px; padding: 4px; border-bottom: 1px solid var(--vscode-panel-border); } + .tools[hidden] { display: none; } + .tools button { background: none; border: none; color: var(--vscode-icon-foreground); padding: 3px; cursor: pointer; border-radius: 4px; display: flex; } + .tools svg { width: 16px; height: 16px; fill: none; stroke: currentColor; stroke-width: 1.3; stroke-linecap: round; stroke-linejoin: round; } + .tools button:hover:not(:disabled) { background: var(--vscode-toolbar-hoverBackground); } + .tools button:disabled { opacity: .4; cursor: default; } + ul { list-style: none; margin: 0; padding: 0; overflow: auto; flex: 1; } + li { padding: 3px 10px; cursor: pointer; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; } + li.sel { background: var(--vscode-list-activeSelectionBackground); color: var(--vscode-list-activeSelectionForeground); } + li:hover:not(.sel) { background: var(--vscode-list-hoverBackground); } + li.off, li.preset { opacity: .65; } + li .tag { opacity: .7; font-size: .9em; margin-left: 6px; } + li.group { cursor: default; opacity: .7; font-size: .85em; text-transform: uppercase; padding-top: 10px; } + li.group:hover { background: none; } + .form { flex: 1; overflow: auto; padding: 16px 20px; } + .grid { display: grid; grid-template-columns: max-content 1fr; gap: 8px 14px; align-items: center; max-width: 820px; } + .grid > label { text-align: right; } + .grid .full { grid-column: 2; display: flex; align-items: center; gap: 6px; } + input[type=text], textarea { width: 100%; box-sizing: border-box; background: var(--vscode-input-background); color: var(--vscode-input-foreground); border: 1px solid var(--vscode-input-border, transparent); padding: 4px 6px; font: inherit; border-radius: 2px; } + textarea { font-family: var(--vscode-editor-font-family); min-height: 52px; resize: vertical; } + select { background: var(--vscode-dropdown-background); color: var(--vscode-dropdown-foreground); border: 1px solid var(--vscode-dropdown-border, transparent); padding: 3px 4px; font: inherit; border-radius: 2px; } + input:focus, textarea:focus, select:focus { outline: 1px solid var(--vscode-focusBorder); } + .grid .sec { grid-column: 1 / -1; margin: 10px 0 0; padding-bottom: 3px; font-weight: 600; border-bottom: 1px solid var(--vscode-panel-border); } + .grid .radios { display: flex; gap: 16px; } + .grid .radios label, .grid .check { display: flex; align-items: center; gap: 5px; } + input:disabled, textarea:disabled { opacity: .7; } + .hint { opacity: .7; font-size: .9em; } + .note { margin-bottom: 12px; padding: 6px 10px; border-left: 3px solid var(--vscode-textLink-foreground); background: var(--vscode-textBlockQuote-background); } + .empty { opacity: .7; padding: 20px; } + .foot { display: flex; gap: 8px; justify-content: flex-end; align-items: center; padding: 10px 16px; border-top: 1px solid var(--vscode-panel-border); } + .foot .err { color: var(--vscode-errorForeground); margin-right: auto; } + .foot button { padding: 4px 14px; border: 1px solid var(--vscode-button-border, transparent); border-radius: 2px; cursor: pointer; font: inherit; background: var(--vscode-button-secondaryBackground); color: var(--vscode-button-secondaryForeground); } + .foot button.primary { background: var(--vscode-button-background); color: var(--vscode-button-foreground); } + .foot button:disabled { opacity: .5; cursor: default; } +`; + +/** The whole page: `noun` names an item in the buttons' tooltips, `extra` goes after the frame (datalists), then the script; `tools: false`, no add/remove/copy/move. */ +export function editorPage(webview: vscode.Webview, nonce: string, opts: { noun: string; extra?: string; script: string; tools?: boolean }): string { + const { noun } = opts; + return ` + + + + + + + +
+
+
+
+ + + + + +
+
    +
    +
    +
    +
    + + + + +
    +
    +${opts.extra ?? ""} + + +`; +} + +/** The extension's icon for an editor's tab. */ +export function editorIcon(): vscode.Uri { + return vscode.Uri.joinPath(vscode.extensions.getExtension("cernik.vs-code-cmake-helper")?.extensionUri ?? vscode.Uri.file("/"), "media", "cmake.svg"); +} diff --git a/src/extension.ts b/src/extension.ts new file mode 100644 index 0000000..aeb14af --- /dev/null +++ b/src/extension.ts @@ -0,0 +1,357 @@ +/** + * CMake Helper: working with a CMake project the way CLion does. Profiles + * (and presets), the targets with their sources, one active target to build, + * run and debug from the status bar, run configurations per target, CTest in + * the Test Explorer, and a reload when a CMake file changes. + */ +import * as path from "node:path"; +import * as vscode from "vscode"; +import { type Target, executableOf } from "./fileApi"; +import { LensProvider } from "./codeLens"; +import { debugTarget, programOf, runTarget } from "./launcher"; +import { openRunConfigEditor } from "./runConfigEditor"; +import { openGdbEditor } from "./gdbEditor"; +import { type GdbConfiguration, KINDS, debugWithConfiguration, gdbConfigurations, runWithConfiguration } from "./gdbConfigs"; +import { openProfileEditor } from "./profileEditor"; +import { removeProfile } from "./profiles"; +import { CMakeProject } from "./project"; +import { StatusBar } from "./statusBar"; +import { TitleBar } from "./titleBar"; +import { CTestProvider } from "./tests"; +import { Updates } from "./updates"; +import { ProfilesProvider, type ProfileNode, type TargetNode, TargetsProvider } from "./views"; +import { exists, expand, pathKey, settings } from "./util"; + +let project: CMakeProject | undefined; + +export function activate(context: vscode.ExtensionContext): void { + const output = vscode.window.createOutputChannel("CMake", "log"); + const current = () => project; + const targetsView = new TargetsProvider(current); + const profilesView = new ProfilesProvider(current); + const lenses = new LensProvider(current); + const statusBar = new StatusBar(); + const ctest = new CTestProvider(current); + const targetsTree = vscode.window.createTreeView("cmakeHelper.targets", { treeDataProvider: targetsView, showCollapseAll: true }); + const profilesTree = vscode.window.createTreeView("cmakeHelper.profiles", { treeDataProvider: profilesView }); + let projectSubs: vscode.Disposable[] = []; + + const refresh = () => { + const p = project; + void vscode.commands.executeCommand("setContext", "cmakeHelper.hasProject", !!p); + void vscode.commands.executeCommand("setContext", "cmakeHelper.loaded", !!p?.model); + void vscode.commands.executeCommand("setContext", "cmakeHelper.busy", !!p && p.state !== "idle"); + void vscode.commands.executeCommand("setContext", "cmakeHelper.needsReload", !!p && (p.needsReload || !p.model) && p.state !== "configuring"); + // The CMakeLists.txt the project is: its context menu reloads it, any other's loads that one instead. + void vscode.commands.executeCommand("setContext", "cmakeHelper.loadedCMakeLists", p ? [path.join(p.sourceDir, "CMakeLists.txt")] : []); + targetsTree.description = p ? `${p.activeProfile.name}${p.needsReload ? " · needs reload" : ""}` : undefined; + targetsTree.message = p?.state === "configuring" && !p.model ? "Loading the CMake project…" : undefined; + targetsView.refresh(); + profilesView.refresh(); + lenses.refresh(); + statusBar.update(p); + }; + + let notified = false; + /** A CMakeLists.txt's folder "Load CMake Project" asked for: configured once the project there is open. */ + let loading: string | undefined; + const openProject = () => { + projectSubs.forEach((d) => d.dispose()); + projectSubs = []; + project?.dispose(); + project = undefined; + const folder = (vscode.workspace.workspaceFolders ?? []).find((f) => { + const dir = path.resolve(f.uri.fsPath, expand(settings().get("sourceDirectory") || "${workspaceFolder}", { workspaceFolder: f.uri.fsPath })); + return exists(path.join(dir, "CMakeLists.txt")); + }); + if (folder) { + const p = new CMakeProject(context, folder, output); + project = p; + projectSubs.push( + p.onDidChange(() => { + refresh(); + // CLion's "CMake project needs to be reloaded": once per change. + if (p.needsReload && !notified) { + notified = true; + void vscode.window.showInformationMessage("CMake files changed. Reload the project?", "Reload", "Always Reload").then(async (a) => { + if (a === "Always Reload") await settings().update("autoReload", "always", vscode.ConfigurationTarget.Global); + if (a) await p.configure(); + }); + } + if (!p.needsReload) notified = false; + }), + p.onDidLoadModel(() => void ctest.discover()), + p.onDidBuild(() => ctest.discoverSoon()), + ); + // Loaded from a CMakeLists.txt's menu: configured now, as CLion does; else what the last configure left, if anything. + if (loading && pathKey(loading) === pathKey(p.sourceDir)) void p.configure(); + else void p.open(); + loading = undefined; + } + refresh(); + if (!project) void ctest.discover(); + }; + + /** A command's target: a name (code lens), a tree node, or the active one. */ + const targetOf = async (arg: unknown, executable = false): Promise => { + const p = project; + if (!p) { + void vscode.window.showWarningMessage("There's no CMake project (CMakeLists.txt) in this workspace."); + return; + } + if (!p.model && !(await p.configure())) return; + if (typeof arg === "string") return p.target(arg); + if (arg && typeof arg === "object" && "target" in arg) return (arg as { target: Target }).target; + const active = p.activeTarget; + if (active && (!executable || active.type === "EXECUTABLE")) return active; + return pickTarget(p, executable); + }; + + const withProject = (fn: (p: CMakeProject) => unknown) => () => { + if (project) return fn(project); + void vscode.window.showWarningMessage("There's no CMake project (CMakeLists.txt) in this workspace."); + }; + + const command = (id: string, fn: (...args: any[]) => unknown) => context.subscriptions.push(vscode.commands.registerCommand(id, fn)); + + command("cmakeHelper.reload", withProject((p) => p.configure())); + // CLion's "Load CMake Project" on a CMakeLists.txt: that folder becomes the project (cmakeHelper.sourceDirectory), and it's configured. + command("cmakeHelper.loadProject", async (arg?: unknown) => { + const uri = arg instanceof vscode.Uri ? arg : vscode.window.activeTextEditor?.document.uri; + if (!uri || path.basename(uri.fsPath) !== "CMakeLists.txt") { + void vscode.window.showWarningMessage("Load CMake Project works on a CMakeLists.txt: open one, or use it from one's context menu."); + return; + } + const dir = path.dirname(uri.fsPath); + if (project && pathKey(project.sourceDir) === pathKey(dir)) return void project.configure(); + const folder = vscode.workspace.getWorkspaceFolder(uri); + if (!folder) { + void vscode.window.showWarningMessage(`${uri.fsPath} isn't in this workspace's folders.`); + return; + } + const rel = path.relative(folder.uri.fsPath, dir).split(path.sep).join("/"); + const value = rel ? `\${workspaceFolder}/${rel}` : undefined; + loading = dir; + // A change opens the project there (onDidChangeConfiguration → openProject), which then configures it; no change: opened here. + if ((settings().get("sourceDirectory") || undefined) === value) openProject(); + else await settings().update("sourceDirectory", value, vscode.ConfigurationTarget.Workspace); + }); + command("cmakeHelper.resetCacheAndReload", withProject((p) => p.configure({ reset: true }))); + command("cmakeHelper.cancelReload", withProject((p) => p.cancelConfigure())); + command("cmakeHelper.showOutput", () => output.show()); + + const updates = new Updates(context, output); + context.subscriptions.push(updates); + command("cmakeHelper.checkForUpdates", () => updates.check(true)); + command("cmakeHelper.setUpdateToken", () => updates.setToken()); + + const titleBar = new TitleBar(context); + command("cmakeHelper.patchTitleBar", () => titleBar.patch()); + command("cmakeHelper.unpatchTitleBar", () => titleBar.unpatch()); + void titleBar.reconcile(); + + command("cmakeHelper.selectProfile", async (arg?: string | ProfileNode) => { + const p = project; + if (!p) return; + let key = typeof arg === "string" ? arg : arg?.key; + if (!key) { + const items = [ + ...p.profiles.map((x) => ({ + label: `${x.key === p.activeProfile.key ? "$(check) " : ""}${x.name}`, + description: [x.buildType, path.relative(p.sourceDir, x.buildDir), x.source === "preset" ? "preset" : ""].filter(Boolean).join(" · "), + key: x.key, + })), + { label: "", kind: vscode.QuickPickItemKind.Separator, key: "" }, + { label: "$(add) New Profile…", key: "#new" }, + { label: "$(gear) Edit Profiles…", key: "#edit" }, + ]; + const pick = await vscode.window.showQuickPick(items, { title: "CMake Profile" }); + if (!pick) return; + if (pick.key.startsWith("#")) return openProfileEditor(p, { add: pick.key === "#new" }); + key = pick.key; + } + if (key) await p.setActiveProfile(key); + }); + command("cmakeHelper.newProfile", withProject((p) => openProfileEditor(p, { add: true }))); + command("cmakeHelper.editProfiles", (node?: ProfileNode) => { + if (project) openProfileEditor(project, { select: node?.key }); + }); + command("cmakeHelper.editProfilesJson", () => vscode.commands.executeCommand("workbench.action.openWorkspaceSettingsFile")); + command("cmakeHelper.removeProfile", async (node?: ProfileNode) => { + const prof = project?.profiles.find((x) => x.key === node?.key); + if (!prof) return; + const ok = await vscode.window.showWarningMessage(`Remove the profile “${prof.name}”? Its build directory stays.`, { modal: true }, "Remove"); + if (ok) await removeProfile(prof); + }); + command("cmakeHelper.revealBuildDirectory", (node?: ProfileNode) => { + const p = project; + const prof = p?.profiles.find((x) => x.key === node?.key) ?? p?.activeProfile; + if (prof) void vscode.commands.executeCommand("revealFileInOS", vscode.Uri.file(prof.buildDir)); + }); + + command("cmakeHelper.selectTarget", async (arg?: unknown) => { + const p = project; + if (!p) return; + // From an editor's title bar the argument is its file: the picker, as with none. + if (typeof arg === "string" || (arg && typeof arg === "object" && !(arg instanceof vscode.Uri))) { + const t = await targetOf(arg); + if (t) await p.setActiveTarget(t.name); + return; + } + if (!p.model && !(await p.configure())) return; + const pick = await pickTargetOrConfiguration(p); + if (!pick) return; + if ("target" in pick) await p.setActiveTarget(pick.target.name); + else if (pick.gdb) await p.setActiveGdbConfiguration(pick.gdb.name); + else if (pick.action === "run") openRunConfigEditor(p); + else openGdbEditor(p, { add: pick.action === "new" }); + }); + + /** With no target given and a GDB server configuration chosen: its target. */ + const gdbTarget = async (arg: unknown): Promise => { + const gdb = arg === undefined ? project?.activeGdbConfiguration : undefined; + if (gdb?.target) return targetOf(gdb.target); + return targetOf(arg); + }; + + command("cmakeHelper.build", async (arg?: unknown) => { + const t = await gdbTarget(arg); + if (t) await project!.build([t.name]); + }); + command("cmakeHelper.rebuild", async (arg?: unknown) => { + const t = await targetOf(arg); + if (t) await project!.build([t.name], { cleanFirst: true }); + }); + command("cmakeHelper.buildAll", withProject((p) => p.build())); + command("cmakeHelper.rebuildAll", withProject((p) => p.build([], { cleanFirst: true }))); + command("cmakeHelper.clean", withProject((p) => p.build([], { clean: true }))); + // A code lens or a tree item names its target; the toolbar's ▶ and 🐞 (no argument) mean the chosen configuration. + command("cmakeHelper.run", async (arg?: unknown) => { + const gdb = arg === undefined ? project?.activeGdbConfiguration : undefined; + if (gdb) { + if (project!.model || (await project!.configure())) await runWithConfiguration(project!, gdb); + return; + } + // Any target: one without a program to run (a custom target) is built, as CLion does. + const t = await targetOf(arg); + if (t) await runTarget(project!, t); + }); + command("cmakeHelper.debug", async (arg?: unknown) => { + const gdb = arg === undefined ? project?.activeGdbConfiguration : undefined; + if (gdb) { + if (project!.model || (await project!.configure())) await debugWithConfiguration(project!, gdb); + return; + } + const t = await targetOf(arg); + if (t) await debugTarget(project!, t); + }); + command("cmakeHelper.editGdbConfigurations", (name?: string) => { + if (project) openGdbEditor(project, { select: typeof name === "string" ? name : project.activeGdbConfiguration?.name }); + else void vscode.window.showWarningMessage("There's no CMake project (CMakeLists.txt) in this workspace."); + }); + command("cmakeHelper.newGdbConfiguration", withProject((p) => openGdbEditor(p, { add: true }))); + // CLion's Run/Debug Configurations: every target, the given one (or the active one) selected. + command("cmakeHelper.editRunConfiguration", async (arg?: unknown) => { + const p = project; + if (!p) return void vscode.window.showWarningMessage("There's no CMake project (CMakeLists.txt) in this workspace."); + if (!p.model && !(await p.configure())) return; + const name = typeof arg === "string" ? arg : arg && typeof arg === "object" && "target" in arg ? (arg as { target: Target }).target.name : undefined; + openRunConfigEditor(p, name); + }); + command("cmakeHelper.revealDefinition", async (arg?: unknown) => { + const t = await targetOf(arg); + const d = t?.definition; + if (!d) return; + const pos = new vscode.Position(d.line - 1, 0); + await vscode.window.showTextDocument(vscode.Uri.file(d.file), { selection: new vscode.Range(pos, pos) }); + }); + command("cmakeHelper.copyArtifactPath", async (arg?: unknown) => { + const t = await targetOf(arg); + const a = t && (executableOf(t) ?? t.artifacts[0]); + if (a) await vscode.env.clipboard.writeText(a); + }); + + // For launch.json and tasks.json: ${command:cmakeHelper.activeProgram} and the like. + command("cmakeHelper.activeProgram", () => { + const t = project?.activeTarget; + return t ? programOf(project!, t) : undefined; + }); + command("cmakeHelper.activeTarget", () => project?.activeTarget?.name); + command("cmakeHelper.buildDirectory", () => project?.buildDir); + command("cmakeHelper.buildType", () => project?.configuration); + + context.subscriptions.push( + output, + statusBar, + ctest, + targetsTree, + profilesTree, + vscode.languages.registerCodeLensProvider([{ language: "c" }, { language: "cpp" }, { language: "cuda-cpp" }, { language: "objective-c" }, { language: "objective-cpp" }, { language: "cmake" }, { pattern: "**/CMakeLists.txt" }, { pattern: "**/*.cmake" }], lenses), + vscode.workspace.onDidChangeWorkspaceFolders(openProject), + vscode.workspace.onDidChangeConfiguration((e) => { + if (e.affectsConfiguration("cmakeHelper.toolbar")) void titleBar.reconcile(); + if (e.affectsConfiguration("cmakeHelper.sourceDirectory")) openProject(); + else if (e.affectsConfiguration("cmakeHelper")) refresh(); + }), + // A CMakeLists.txt that appears in an empty workspace makes it a project. + vscode.workspace.createFileSystemWatcher("**/CMakeLists.txt", false, true, true).onDidCreate(() => { + if (!project) openProject(); + }), + { dispose: () => projectSubs.forEach((d) => d.dispose()) }, + { dispose: () => project?.dispose() }, + ); + openProject(); +} + +async function pickTarget(p: CMakeProject, executable: boolean): Promise { + const list = p.visibleTargets().filter((t) => !executable || t.type === "EXECUTABLE"); + if (!list.length) { + void vscode.window.showWarningMessage(executable ? "The project has no executables." : "The project has no targets."); + return; + } + const active = p.activeTarget?.name; + const items = list.map((t) => ({ + label: `${t.name === active ? "$(check) " : ""}${t.name}`, + description: t.type === "EXECUTABLE" ? "executable" : t.type.toLowerCase().replace("_", " "), + detail: t.definition ? path.relative(p.sourceDir, t.definition.file) + `:${t.definition.line}` : undefined, + target: t, + })); + const pick = await vscode.window.showQuickPick(items, { title: `CMake Target [${p.activeProfile.name}]`, matchOnDescription: true }); + return pick?.target; +} + +type TargetOrConfiguration = { target: Target } | { gdb: GdbConfiguration; action?: undefined } | { gdb?: undefined; action: "new" | "edit" | "run" }; + +/** The target picker as CLion's run configuration combo: the targets, then the GDB server configurations. */ +async function pickTargetOrConfiguration(p: CMakeProject): Promise { + const gdbActive = p.activeGdbConfiguration?.name; + const active = gdbActive ? undefined : p.activeTarget?.name; + type Item = vscode.QuickPickItem & { value?: TargetOrConfiguration }; + const items: Item[] = p.visibleTargets().map((t) => ({ + label: `${t.name === active ? "$(check) " : ""}${t.name}`, + description: t.type === "EXECUTABLE" ? "executable" : t.type.toLowerCase().replace("_", " "), + detail: t.definition ? path.relative(p.sourceDir, t.definition.file) + `:${t.definition.line}` : undefined, + value: { target: t }, + })); + items.push({ label: "GDB Server Configurations", kind: vscode.QuickPickItemKind.Separator }); + for (const c of gdbConfigurations()) { + items.push({ + label: `${c.name === gdbActive ? "$(check) " : ""}$(${c.kind === "embedded" ? "chip" : "remote"}) ${c.name}`, + description: [KINDS[c.kind], c.target, c.ssh || c.remote].filter(Boolean).join(" · "), + value: { gdb: c }, + }); + } + items.push( + { label: "", kind: vscode.QuickPickItemKind.Separator }, + { label: "$(gear) Edit Configurations…", description: "the targets' run configurations", value: { action: "run" } }, + { label: "$(add) New GDB Server Configuration…", value: { action: "new" } }, + { label: "$(chip) Edit GDB Server Configurations…", value: { action: "edit" } }, + ); + const pick = await vscode.window.showQuickPick(items, { title: `CMake Target [${p.activeProfile.name}]`, matchOnDescription: true }); + return pick?.value; +} + +export function deactivate(): void { + project?.dispose(); + project = undefined; +} diff --git a/src/fileApi.ts b/src/fileApi.ts new file mode 100644 index 0000000..e610397 --- /dev/null +++ b/src/fileApi.ts @@ -0,0 +1,185 @@ +/** + * CMake's File API (cmake-file-api(7)): before configuring, a query file in + * the build directory asks for the code model, the cache, the CMake input + * files and the toolchains; CMake answers with JSON files in reply/, which is + * what CLion reads too. Nothing here parses CMakeLists.txt. + */ +import * as fs from "node:fs"; +import * as path from "node:path"; +import { readJson } from "./util"; + +const CLIENT = "client-cmake-helper"; + +export type TargetType = + | "EXECUTABLE" + | "STATIC_LIBRARY" + | "SHARED_LIBRARY" + | "MODULE_LIBRARY" + | "OBJECT_LIBRARY" + | "INTERFACE_LIBRARY" + | "UTILITY"; + +export interface Location { + file: string; + /** 1-based, as CMake gives it. */ + line: number; +} + +export interface SourceFile { + path: string; + group: string; + generated: boolean; +} + +export interface Target { + id: string; + name: string; + type: TargetType; + /** Absolute paths of what it builds (the executable, the library). */ + artifacts: string[]; + sources: SourceFile[]; + /** Where it's defined: the add_executable / add_library call. */ + definition?: Location; + sourceDir: string; + buildDir: string; + folder?: string; + /** ALL_BUILD, ZERO_CHECK and the like. */ + generatorProvided: boolean; + /** Ids of the targets it depends on. */ + dependencies: string[]; +} + +export interface Model { + sourceDir: string; + buildDir: string; + /** The configuration the targets are of (Debug, Release…; "" for a single-config generator without a build type). */ + configuration: string; + configurations: string[]; + generator: string; + targets: Target[]; + /** The project's own CMake files (CMakeLists.txt, *.cmake), the ones a change to should reload it. */ + inputs: string[]; + cache: Map; + compilerIds: string[]; +} + +export function writeQuery(buildDir: string): void { + const dir = path.join(buildDir, ".cmake", "api", "v1", "query", CLIENT); + fs.mkdirSync(dir, { recursive: true }); + const query = { + requests: [ + { kind: "codemodel", version: 2 }, + { kind: "cache", version: 2 }, + { kind: "cmakeFiles", version: 1 }, + { kind: "toolchains", version: 1 }, + ], + }; + fs.writeFileSync(path.join(dir, "query.json"), JSON.stringify(query, null, 2)); +} + +export function multiConfigGenerator(generator: string): boolean { + return generator === "Ninja Multi-Config" || generator === "Xcode" || generator.startsWith("Visual Studio"); +} + +/** + * The model from the latest reply in `buildDir`, or undefined when there's + * none for our query (never configured, or configured before we asked). + * `configuration` picks one of a multi-config generator's configurations. + */ +export function readReply(buildDir: string, configuration?: string): Model | undefined { + const replyDir = path.join(buildDir, ".cmake", "api", "v1", "reply"); + let indexes: string[]; + try { + indexes = fs.readdirSync(replyDir).filter((f) => /^index-.*\.json$/.test(f)).sort(); + } catch { + return undefined; + } + const last = indexes[indexes.length - 1]; + if (!last) return undefined; + const index = readJson(path.join(replyDir, last)); + const responses: any[] | undefined = index?.reply?.[CLIENT]?.["query.json"]?.responses; + if (!Array.isArray(responses)) return undefined; + const file = (kind: string) => { + const r = responses.find((x) => x?.kind === kind && x.jsonFile); + return r ? readJson(path.join(replyDir, r.jsonFile)) : undefined; + }; + + const codemodel = file("codemodel"); + if (!codemodel) return undefined; + const sourceDir: string = codemodel.paths.source; + const absBuild: string = codemodel.paths.build; + + const cache = new Map(); + for (const e of file("cache")?.entries ?? []) cache.set(e.name, String(e.value)); + + const inputs: string[] = []; + for (const i of file("cmakeFiles")?.inputs ?? []) { + if (i.isExternal || i.isGenerated || i.isCMake) continue; + inputs.push(path.resolve(sourceDir, i.path)); + } + + const compilerIds: string[] = []; + for (const t of file("toolchains")?.toolchains ?? []) if (t.compiler?.id) compilerIds.push(t.compiler.id); + + const configs: any[] = codemodel.configurations ?? []; + const config = configs.find((c) => c.name === configuration) ?? configs[0]; + const targets: Target[] = []; + for (const ref of config?.targets ?? []) { + const t = readJson(path.join(replyDir, ref.jsonFile)); + if (t) targets.push(toTarget(t, sourceDir, absBuild)); + } + targets.sort((a, b) => a.name.localeCompare(b.name)); + + return { + sourceDir, + buildDir: absBuild, + configuration: config?.name ?? "", + configurations: configs.map((c) => c.name), + generator: cache.get("CMAKE_GENERATOR") ?? "", + targets, + inputs, + cache, + compilerIds, + }; +} + +function toTarget(t: any, sourceDir: string, buildDir: string): Target { + const groups: string[] = (t.sourceGroups ?? []).map((g: any) => g.name); + const sources: SourceFile[] = (t.sources ?? []).map((s: any) => ({ + path: path.resolve(sourceDir, s.path), + group: groups[s.sourceGroupIndex] ?? "", + generated: !!s.isGenerated, + })); + return { + id: t.id, + name: t.name, + type: t.type, + artifacts: (t.artifacts ?? []).map((a: any) => path.resolve(buildDir, a.path)), + sources, + definition: definitionOf(t, sourceDir), + sourceDir: path.resolve(sourceDir, t.paths?.source ?? "."), + buildDir: path.resolve(buildDir, t.paths?.build ?? "."), + folder: t.folder?.name, + generatorProvided: !!t.isGeneratorProvided, + dependencies: (t.dependencies ?? []).map((d: any) => d.id), + }; +} + +/** The node the target's backtrace starts at is the add_* call that made it. */ +function definitionOf(t: any, sourceDir: string): Location | undefined { + const graph = t.backtraceGraph; + const node = typeof t.backtrace === "number" ? graph?.nodes?.[t.backtrace] : undefined; + if (!node || typeof node.line !== "number") return undefined; + const file = graph.files?.[node.file]; + return file ? { file: path.resolve(sourceDir, file), line: node.line } : undefined; +} + +/** The executable a target runs as: on Windows its artifacts include the .pdb too. */ +export function executableOf(t: Target): string | undefined { + if (t.type !== "EXECUTABLE") return undefined; + return t.artifacts.find((a) => /\.(exe|com)$/i.test(a)) ?? t.artifacts.find((a) => !/\.(pdb|ilk|lib|exp)$/i.test(a)) ?? t.artifacts[0]; +} + +export function isLibrary(t: Target): boolean { + return t.type.endsWith("_LIBRARY"); +} diff --git a/src/gdbConfigs.ts b/src/gdbConfigs.ts new file mode 100644 index 0000000..e4b2401 --- /dev/null +++ b/src/gdbConfigs.ts @@ -0,0 +1,431 @@ +/** + * GDB server configurations, as CLion's run configurations of those kinds: + * + * - Embedded GDB Server: a GDB server started here (OpenOCD, J-Link, ST-LINK, + * pyOCD…), gdb connected to it, the program downloaded (flashed) and the + * board reset. Run is CLion's "Download & Run": flash and start, no debugger. + * - Remote GDB Server: gdb connected to a gdbserver elsewhere, with a sysroot + * and path mappings; optionally the program uploaded over SSH first and + * gdbserver started there. Run then runs it there over SSH. + * + * They're kept in the setting `cmakeHelper.gdbConfigurations`; debugging goes + * through C/C++ (cppdbg), which starts the server and connects by itself. + */ +import * as crypto from "node:crypto"; +import * as fs from "node:fs"; +import * as path from "node:path"; +import * as vscode from "vscode"; +import { type Target, executableOf } from "./fileApi"; +import { buildFirst } from "./launcher"; +import type { CMakeProject } from "./project"; +import { errorText, expand, onPath, parseEnv, settings, splitArgs } from "./util"; + +export type GdbKind = "embedded" | "remote"; +export type Download = "always" | "updated" | "never"; + +export interface GdbConfiguration { + name: string; + kind: GdbKind; + /** The CMake target whose executable is debugged (its symbols) and flashed or uploaded; none: the active target. */ + target?: string; + /** A file instead of the target's (an .elf built some other way). */ + program?: string; + /** gdb: one that knows the board's architecture (gdb-multiarch, arm-none-eabi-gdb…). */ + debugger?: string; + /** What `target remote` gets: localhost:3333, tcp:192.168.1.5:2345… */ + remote: string; + /** Embedded: the GDB server started first, its arguments, and the text it prints once it listens. */ + server?: string; + serverArgs?: string; + serverReady?: string; + serverEnvironment?: Record; + /** Embedded: flash the program when debugging: always, only when it changed since the last time, or never. */ + download?: Download; + /** Embedded: run before downloading (and to start it after "Download & Run"'s download). */ + resetCommand?: string; + /** Embedded, Download & Run: what starts the program after the download. */ + runCommands?: string[]; + /** Remote: the target's root, for its shared libraries. */ + sysroot?: string; + /** Remote: where its sources were built (as gdb sees them) → where they are here. */ + pathMappings?: Record; + /** Remote over SSH: user@host; the program is uploaded to `remotePath` and gdbserver started there. */ + ssh?: string; + remotePath?: string; + gdbserver?: string; + /** The program's arguments (remote over SSH). */ + args?: string; + /** gdb commands before connecting, and after connecting (before the program goes on). */ + initCommands?: string[]; + connectCommands?: string[]; + buildBeforeDebug?: boolean; +} + +export const KINDS: Record = { embedded: "Embedded GDB Server", remote: "Remote GDB Server" }; + +export function gdbConfigurations(): GdbConfiguration[] { + const list = settings().get("gdbConfigurations") ?? []; + return list.filter((c) => c && typeof c.name === "string" && c.name.trim() && (c.kind === "embedded" || c.kind === "remote")); +} + +export function gdbConfiguration(name: string | undefined): GdbConfiguration | undefined { + return name ? gdbConfigurations().find((c) => c.name === name) : undefined; +} + +/** The debugger when the configuration names none: one for any architecture if there is one. */ +export function defaultDebugger(): string { + for (const g of ["gdb-multiarch", "arm-none-eabi-gdb", "gdb"]) if (onPath(g)) return g; + return "gdb"; +} + +/** A program on PATH (or a path) as a full path: C/C++ starts the server and gdb without a shell. */ +function resolveProgram(p: string): string | undefined { + if (p.includes("/") || p.includes("\\")) return fs.existsSync(p) ? p : undefined; + for (const dir of (process.env.PATH ?? "").split(path.delimiter)) { + const full = dir && path.join(dir, p); + if (full && fs.existsSync(full)) return full; + } + return undefined; +} + +/** user@host:2345 → host:2345 for `target remote`, from the SSH host and the port in `remote`. */ +function sshRemote(c: GdbConfiguration): { host: string; port: string } { + const host = (c.ssh ?? "").replace(/^.*@/, "").replace(/:\d+$/, ""); + const port = /:(\d+)\s*$/.exec(c.remote)?.[1] ?? "2345"; + return { host, port }; +} + +interface Resolved { + target?: Target; + program: string; + vars: ReturnType & { program: string }; +} + +/** The configuration's program (after building it, if it's a target and that's on). */ +async function resolve(project: CMakeProject, c: GdbConfiguration, build: boolean): Promise { + const target = c.target ? project.target(c.target) : project.activeTarget; + if (c.target && !target && !c.program) { + void vscode.window.showErrorMessage(`“${c.name}”: the project has no target ${c.target}.`); + return; + } + if (build && target && (c.buildBeforeDebug ?? true) && !(await buildFirst(project, target))) return; + const base = project.vars(target); + const program = c.program ? path.resolve(project.sourceDir, expand(c.program, base)) : target && executableOf(target); + if (!program) { + void vscode.window.showErrorMessage(`“${c.name}” has no program: choose an executable target, or a file.`); + return; + } + if (!fs.existsSync(program)) { + void vscode.window.showErrorMessage(`${program} isn't there. Build it first.`); + return; + } + return { target, program, vars: { ...base, program } }; +} + +const ex = (s: string, vars: Resolved["vars"]) => expand(s, vars as Parameters[1]).replace(/\$\{program\}/g, vars.program); + +/** The program's content, to tell whether it changed since it was last downloaded. */ +function fingerprint(file: string): string { + return crypto.createHash("sha1").update(fs.readFileSync(file)).digest("hex"); +} + +const sh = (a: string) => `'${a.replace(/'/g, `'\\''`)}'`; + +const downloadedKey = (c: GdbConfiguration) => `cmakeHelper.downloaded:${c.name}`; + +/** 🐞 with a configuration: (build,) (upload,) start the server, connect gdb, (download,) go. */ +export async function debugWithConfiguration(project: CMakeProject, c: GdbConfiguration): Promise { + const r = await resolve(project, c, true); + if (!r) return false; + const gdbName = c.debugger?.trim() || defaultDebugger(); + const gdb = resolveProgram(ex(gdbName, r.vars)); + if (!gdb) { + void vscode.window.showErrorMessage(`“${c.name}” debugs with ${gdbName}, and it isn't installed here (not on PATH). Set the debugger in the configuration.`, "Edit Configuration").then((a) => { + if (a) void vscode.commands.executeCommand("cmakeHelper.editGdbConfigurations", c.name); + }); + return false; + } + if (!vscode.extensions.getExtension("ms-vscode.cpptools")) { + void vscode.window.showErrorMessage("Remote and embedded debugging go through the C/C++ extension (ms-vscode.cpptools): install it."); + return false; + } + + const setup: { text: string; ignoreFailures?: boolean; description?: string }[] = [ + { description: "Pretty printing", text: "-enable-pretty-printing", ignoreFailures: true }, + ]; + const connect: { text: string; ignoreFailures?: boolean }[] = []; + const config: vscode.DebugConfiguration = { + type: "cppdbg", + request: "launch", + name: c.name, + program: r.program, + args: [], + cwd: project.sourceDir, + MIMode: "gdb", + miDebuggerPath: gdb, + externalConsole: false, + stopAtEntry: false, + }; + + if (c.kind === "embedded") { + config.miDebuggerServerAddress = ex(c.remote, r.vars).replace(/^tcp:/, ""); + if (c.server?.trim()) { + const server = resolveProgram(ex(c.server.trim(), r.vars)); + if (!server) { + void vscode.window.showErrorMessage(`“${c.name}”: the GDB server ${c.server} isn't installed here (not on PATH).`); + return false; + } + // Its environment through env(1): C/C++'s own `environment` is the program's. + const env = Object.entries(c.serverEnvironment ?? {}).map(([k, v]) => `${k}=${ex(v, r.vars)}`); + config.debugServerPath = env.length ? (resolveProgram("env") ?? "/usr/bin/env") : server; + config.debugServerArgs = [...env.map(sh), ...(env.length ? [sh(server)] : []), ex(c.serverArgs ?? "", r.vars)].filter(Boolean).join(" "); + // Without the text it prints once it listens, C/C++ only waits a moment. + if (c.serverReady?.trim()) config.serverStarted = c.serverReady.trim(); + config.filterStdout = true; + config.filterStderr = true; + config.serverLaunchTimeout = 20000; + } + const reset = c.resetCommand?.trim(); + const mode = c.download ?? "always"; + const print = fingerprint(r.program); + const download = mode === "always" || (mode === "updated" && project.workspaceState.get(downloadedKey(c)) !== print); + if (reset) connect.push({ text: reset }); + if (download) { + connect.push({ text: "load" }); + if (reset) connect.push({ text: reset }); + await project.workspaceState.update(downloadedKey(c), print); + } + } else { + if (c.sysroot?.trim()) setup.push({ text: `set sysroot ${ex(c.sysroot.trim(), r.vars)}` }); + if (c.ssh?.trim()) { + const remotePath = c.remotePath?.trim() || `/tmp/${path.basename(r.program)}`; + if (!(await upload(project, c, r.program, remotePath))) return false; + const { host, port } = sshRemote(c); + config.miDebuggerServerAddress = `${host}:${port}`; + config.debugServerPath = resolveProgram("ssh") ?? "ssh"; + config.debugServerArgs = ["-o", "BatchMode=yes", c.ssh.trim(), c.gdbserver?.trim() || "gdbserver", `:${port}`, remotePath, ...splitArgs(ex(c.args ?? "", r.vars))] + .map((a) => (/[\s"'\\]/.test(a) ? `'${a.replace(/'/g, `'\\''`)}'` : a)) + .join(" "); + config.serverStarted = "Listening on port"; + config.filterStdout = true; + config.filterStderr = true; + config.serverLaunchTimeout = 20000; + } else { + config.miDebuggerServerAddress = ex(c.remote, r.vars).replace(/^tcp:/, ""); + } + if (c.pathMappings && Object.keys(c.pathMappings).length) { + config.sourceFileMap = Object.fromEntries(Object.entries(c.pathMappings).map(([from, to]) => [from, path.resolve(project.sourceDir, ex(to, r.vars))])); + } + } + for (const t of c.initCommands ?? []) if (t.trim()) setup.push({ text: ex(t.trim(), r.vars) }); + for (const t of c.connectCommands ?? []) if (t.trim()) connect.push({ text: ex(t.trim(), r.vars) }); + config.setupCommands = setup; + if (connect.length) config.postRemoteConnectCommands = connect; + // The owner's additions for cppdbg apply here too (e.g. a longer timeout). + const extra = settings().get>>("debugConfiguration")?.cppdbg; + try { + return await vscode.debug.startDebugging(project.folder, extra ? { ...config, ...extra, miDebuggerPath: gdb } : config); + } catch (err) { + void vscode.window.showErrorMessage(`Can't start the debugger: ${errorText(err)}`); + return false; + } +} + +/** Remote over SSH: the program copied to the target (scp), in the build terminal. */ +async function upload(project: CMakeProject, c: GdbConfiguration, program: string, remotePath: string): Promise { + const scp = resolveProgram("scp"); + if (!scp) { + void vscode.window.showErrorMessage("Uploading needs scp, and it isn't installed here."); + return false; + } + const code = await project.runTask(`Upload ${path.basename(program)} → ${c.ssh}`, "build", scp, ["-o", "BatchMode=yes", program, `${c.ssh!.trim()}:${remotePath}`], { + cwd: project.sourceDir, + key: `upload:${c.name}`, + }); + if (code === 0) return true; + void vscode.window.showErrorMessage(`Uploading to ${c.ssh} failed (see the terminal).`); + return false; +} + +/** + * ▶ with a configuration. Embedded: CLion's "Download & Run" (start the server, + * flash with gdb in batch mode, start the program, detach). Remote over SSH: + * upload and run it there. Remote without SSH: only the debugger reaches it. + */ +export async function runWithConfiguration(project: CMakeProject, c: GdbConfiguration): Promise { + if (c.kind === "remote" && !c.ssh?.trim()) { + void vscode.window.showInformationMessage(`“${c.name}” reaches its target only through gdb: debug it (Shift+F9), or set an SSH host to run it there.`); + return; + } + const r = await resolve(project, c, true); + if (!r) return; + if (c.kind === "remote") { + const remotePath = c.remotePath?.trim() || `/tmp/${path.basename(r.program)}`; + if (!(await upload(project, c, r.program, remotePath))) return; + const ssh = resolveProgram("ssh") ?? "ssh"; + await project.runTask(`Run ${c.name}`, "run", ssh, ["-t", c.ssh!.trim(), [remotePath, ...splitArgs(ex(c.args ?? "", r.vars))].map(sh).join(" ")], { + cwd: project.sourceDir, + key: `run:${c.name}`, + }); + return; + } + const gdbName = c.debugger?.trim() || defaultDebugger(); + const gdb = resolveProgram(ex(gdbName, r.vars)); + if (!gdb) { + void vscode.window.showErrorMessage(`Download & Run needs ${gdbName}, and it isn't installed here (not on PATH).`); + return; + } + const reset = c.resetCommand?.trim(); + const run = (c.runCommands?.length ? c.runCommands : ["monitor reset run"]).map((t) => ex(t, r.vars)).filter((t) => t.trim()); + const gdbArgs = ["-batch", "-nx", ...(c.initCommands ?? []).flatMap((t) => ["-ex", ex(t, r.vars)]), "-ex", `target remote ${ex(c.remote, r.vars).replace(/^tcp:/, "")}`]; + if (reset) gdbArgs.push("-ex", reset); + gdbArgs.push("-ex", "load"); + for (const t of run) gdbArgs.push("-ex", t); + gdbArgs.push("-ex", "detach", r.program); + // The server for the while gdb flashes: started first, waited for (its "ready" text, else a moment), stopped after. + const lines = ["set -e"]; + if (c.server?.trim()) { + const server = resolveProgram(ex(c.server.trim(), r.vars)); + if (!server) { + void vscode.window.showErrorMessage(`“${c.name}”: the GDB server ${c.server} isn't installed here (not on PATH).`); + return; + } + const log = `/tmp/cmake-helper-server-${process.pid}.log`; + lines.push( + `${[server, ...splitArgs(ex(c.serverArgs ?? "", r.vars))].map(sh).join(" ")} > ${sh(log)} 2>&1 &`, + "server=$!", + `trap 'kill $server 2>/dev/null; cat ${sh(log)}; rm -f ${sh(log)}' EXIT`, + c.serverReady?.trim() + ? `for i in $(seq 1 100); do grep -qE ${sh(c.serverReady.trim())} ${sh(log)} && break; kill -0 $server 2>/dev/null || { echo "The GDB server stopped:"; exit 1; }; sleep 0.2; done` + : "sleep 1", + ); + } + lines.push([gdb, ...gdbArgs].map(sh).join(" ")); + const env = Object.fromEntries(Object.entries(c.serverEnvironment ?? {}).map(([k, v]) => [k, ex(v, r.vars)])); + await project.runTask(`Download & Run ${c.name}`, "run", "/bin/sh", ["-c", lines.join("\n")], { cwd: project.sourceDir, env, key: `run:${c.name}` }); + await project.workspaceState.update(downloadedKey(c), fingerprint(r.program)); +} + +/* ---- the configuration as the editor's form shows it ---- */ + +export interface GdbRow { + id: string; + origName?: string; + name: string; + kind: GdbKind; + target: string; + program: string; + debugger: string; + remote: string; + server: string; + serverArgs: string; + serverReady: string; + serverEnvironment: string; + download: Download; + resetCommand: string; + runCommands: string; + sysroot: string; + pathMappings: string; + ssh: string; + remotePath: string; + gdbserver: string; + args: string; + initCommands: string; + connectCommands: string; + buildBeforeDebug: boolean; + rest: Record; +} + +const SHOWN = ["name", "kind", "target", "program", "debugger", "remote", "server", "serverArgs", "serverReady", "serverEnvironment", "download", "resetCommand", "runCommands", "sysroot", "pathMappings", "ssh", "remotePath", "gdbserver", "args", "initCommands", "connectCommands", "buildBeforeDebug"]; + +const lines = (l: string[] | undefined) => (l ?? []).join("\n"); +const unlines = (s: string) => s.split("\n").map((x) => x.trim()).filter(Boolean); +const mappings = (m: Record | undefined) => Object.entries(m ?? {}).map(([a, b]) => `${a}=${b}`).join("\n"); + +export function toRow(c: GdbConfiguration): GdbRow { + const rest: Record = {}; + for (const [k, v] of Object.entries(c)) if (!SHOWN.includes(k)) rest[k] = v; + return { + id: crypto.randomUUID(), + origName: c.name, + name: c.name, + kind: c.kind, + target: c.target ?? "", + program: c.program ?? "", + debugger: c.debugger ?? "", + remote: c.remote ?? "", + server: c.server ?? "", + serverArgs: c.serverArgs ?? "", + serverReady: c.serverReady ?? "", + serverEnvironment: Object.entries(c.serverEnvironment ?? {}).map(([k, v]) => `${k}=${v}`).join(";"), + download: c.download ?? "always", + resetCommand: c.resetCommand ?? "", + runCommands: lines(c.runCommands), + sysroot: c.sysroot ?? "", + pathMappings: mappings(c.pathMappings), + ssh: c.ssh ?? "", + remotePath: c.remotePath ?? "", + gdbserver: c.gdbserver ?? "", + args: c.args ?? "", + initCommands: lines(c.initCommands), + connectCommands: lines(c.connectCommands), + buildBeforeDebug: c.buildBeforeDebug ?? true, + rest, + }; +} + +/** The row as the setting keeps it: only what's set, and only the fields of its kind. */ +export function fromRow(r: GdbRow): GdbConfiguration { + const c: GdbConfiguration = { ...(r.rest as object), name: r.name.trim(), kind: r.kind, remote: r.remote.trim() }; + const set = (k: K, v: GdbConfiguration[K] | "" | undefined) => { + if (v !== "" && v !== undefined && !(Array.isArray(v) && !v.length) && !(typeof v === "object" && v && !Array.isArray(v) && !Object.keys(v).length)) c[k] = v as GdbConfiguration[K]; + }; + set("target", r.target.trim()); + set("program", r.program.trim()); + set("debugger", r.debugger.trim()); + set("initCommands", unlines(r.initCommands)); + set("connectCommands", unlines(r.connectCommands)); + if (!r.buildBeforeDebug) c.buildBeforeDebug = false; + if (r.kind === "embedded") { + set("server", r.server.trim()); + set("serverArgs", r.serverArgs.trim()); + set("serverReady", r.serverReady.trim()); + set("serverEnvironment", parseEnv(r.serverEnvironment.trim())); + c.download = r.download; + set("resetCommand", r.resetCommand.trim()); + set("runCommands", unlines(r.runCommands)); + } else { + set("sysroot", r.sysroot.trim()); + const map: Record = {}; + for (const l of unlines(r.pathMappings)) { + const i = l.indexOf("="); + if (i > 0) map[l.slice(0, i).trim()] = l.slice(i + 1).trim(); + } + set("pathMappings", map); + set("ssh", r.ssh.trim()); + set("remotePath", r.remotePath.trim()); + set("gdbserver", r.gdbserver.trim()); + set("args", r.args.trim()); + } + return c; +} + +/** What a list can't be saved with: the first problem, or none. */ +export function problem(rows: GdbRow[]): string | undefined { + const seen = new Set(); + for (const r of rows) { + const name = r.name.trim(); + if (!name) return "Every configuration needs a name."; + if (seen.has(name)) return `There are two configurations named “${name}”.`; + seen.add(name); + if (!r.remote.trim() && !(r.kind === "remote" && r.ssh.trim())) return `“${name}” needs the 'target remote' address (e.g. localhost:3333).`; + if (r.serverReady.trim()) { + try { + new RegExp(r.serverReady.trim()); + } catch { + return `“${name}”: the server's "ready" text isn't a valid regular expression.`; + } + } + } + return undefined; +} diff --git a/src/gdbEditor.ts b/src/gdbEditor.ts new file mode 100644 index 0000000..1fb73a7 --- /dev/null +++ b/src/gdbEditor.ts @@ -0,0 +1,274 @@ +/** + * The GDB server configurations editor, as CLion's Run/Debug Configurations + * for "Embedded GDB Server" and "Remote GDB Server": the list on the left, the + * selected one's fields on the right (those of its kind), OK / Cancel / Apply. + * It edits the workspace setting `cmakeHelper.gdbConfigurations`. + */ +import * as crypto from "node:crypto"; +import * as vscode from "vscode"; +import { editorIcon, editorPage } from "./editorPage"; +import { type GdbRow, defaultDebugger, fromRow, gdbConfigurations, problem, toRow } from "./gdbConfigs"; +import type { CMakeProject } from "./project"; +import { settings } from "./util"; + +let panel: vscode.WebviewPanel | undefined; + +/** Opens the editor (or brings it forward). `select`: a configuration's name; `add`: start with a new one. */ +export function openGdbEditor(project: CMakeProject, opts: { select?: string; add?: boolean } = {}): void { + const init = () => ({ + type: "init", + rows: gdbConfigurations().map(toRow), + targets: project.visibleTargets().filter((t) => t.type === "EXECUTABLE").map((t) => t.name), + activeTarget: project.activeTarget?.name ?? "", + active: project.activeGdbConfiguration?.name ?? "", + defaultDebugger: defaultDebugger(), + select: opts.select, + add: !!opts.add, + }); + if (panel) { + panel.reveal(); + void panel.webview.postMessage(init()); + return; + } + panel = vscode.window.createWebviewPanel("cmakeHelper.gdbConfigurations", "GDB Server Configurations", vscode.ViewColumn.Active, { enableScripts: true, retainContextWhenHidden: true }); + panel.iconPath = editorIcon(); + const nonce = crypto.randomBytes(16).toString("base64"); + panel.webview.html = editorPage(panel.webview, nonce, { noun: "configuration", extra: DATALISTS, script: SCRIPT }); + panel.onDidDispose(() => (panel = undefined)); + panel.webview.onDidReceiveMessage(async (msg: { type: string; rows?: GdbRow[]; close?: boolean }) => { + if (msg.type === "ready") { + void panel?.webview.postMessage(init()); + } else if (msg.type === "cancel") { + panel?.dispose(); + } else if (msg.type === "save" && msg.rows) { + const err = problem(msg.rows); + if (err) { + void panel?.webview.postMessage({ type: "error", text: err }); + return; + } + // The chosen configuration stays chosen under its new name; a removed one isn't chosen any more. + const active = project.activeGdbConfiguration?.name; + if (active) { + const row = msg.rows.find((r) => r.origName === active); + if (!row) await project.setActiveGdbConfiguration(undefined); + else if (row.name.trim() !== active) await project.setActiveGdbConfiguration(row.name.trim()); + } + try { + await settings().update("gdbConfigurations", msg.rows.map(fromRow), vscode.ConfigurationTarget.Workspace); + } catch (e) { + void panel?.webview.postMessage({ type: "error", text: e instanceof Error ? e.message : String(e) }); + return; + } + if (msg.close) panel?.dispose(); + else void panel?.webview.postMessage({ ...init(), type: "saved" }); + } + }); +} + +const DATALISTS = ` +`; + +/** The editor's script: the list, the form of the selected configuration's kind, and the presets for common GDB servers. */ +const SCRIPT = String.raw` +const vscode = acquireVsCodeApi(); +let rows = [], sel = null, dirty = false, active = "", defaultDebugger = "gdb", activeTarget = ""; +const $ = (id) => document.getElementById(id); +const esc = (s) => String(s ?? "").replace(/[&<>"']/g, (c) => ({ "&": "&", "<": "<", ">": ">", '"': """, "'": "'" }[c])); +const uid = () => Math.random().toString(36).slice(2) + Date.now().toString(36); +const current = () => rows.find((r) => r.id === sel); + +/* Starting points for common servers (CLion's GDB server field); the board's own files and names go in after. */ +const PRESETS = { + openocd: { label: "OpenOCD", server: "openocd", serverArgs: "-f interface/stlink.cfg -f target/stm32f4x.cfg", serverReady: "Listening on port 3333 for gdb connections", remote: "localhost:3333", resetCommand: "monitor reset halt", runCommands: "monitor reset run" }, + jlink: { label: "SEGGER J-Link", server: "JLinkGDBServerCLExe", serverArgs: "-device STM32F407VG -if SWD -speed 4000 -port 2331 -nogui -silent", serverReady: "Waiting for GDB connection", remote: "localhost:2331", resetCommand: "monitor reset", runCommands: "monitor reset\nmonitor go" }, + stlink: { label: "ST-LINK (st-util)", server: "st-util", serverArgs: "-p 4242", serverReady: "Listening at", remote: "localhost:4242", resetCommand: "monitor reset halt", runCommands: "monitor reset" }, + pyocd: { label: "pyOCD", server: "pyocd", serverArgs: "gdbserver --target stm32f407vg --port 3333", serverReady: "GDB server listening on port", remote: "localhost:3333", resetCommand: "monitor reset halt", runCommands: "monitor reset" }, +}; + +const EMPTY = { kind: "embedded", target: "", program: "", debugger: "", remote: "", server: "", serverArgs: "", serverReady: "", serverEnvironment: "", download: "always", resetCommand: "", runCommands: "", sysroot: "", pathMappings: "", ssh: "", remotePath: "", gdbserver: "", args: "", initCommands: "", connectCommands: "", buildBeforeDebug: true, rest: {} }; + +function freeName(base) { + const taken = new Set(rows.map((r) => r.name.trim())); + if (!taken.has(base)) return base; + for (let i = 2; ; i++) if (!taken.has(base + " " + i)) return base + " " + i; +} + +function setDirty(v) { + dirty = v; + $("apply").disabled = !v; + if (v) $("err").textContent = ""; +} + +function renderList() { + $("list").innerHTML = rows.map((r) => { + const tag = r.origName && r.origName === active ? 'chosen' : '' + (r.kind === "embedded" ? "embedded" : "remote") + ""; + return '
  • ' + esc(r.name || "(unnamed)") + tag + "
  • "; + }).join(""); + const r = current(); + const i = rows.indexOf(r); + $("remove").disabled = !r; + $("copy").disabled = !r; + $("up").disabled = !r || i <= 0; + $("down").disabled = !r || i >= rows.length - 1; +} + +function input(label, key, value, o = {}) { + const attrs = (o.list ? ' list="' + o.list + '"' : "") + (o.placeholder ? ' placeholder="' + esc(o.placeholder) + '"' : ""); + const field = o.area + ? '" + : '"; + return "
    " + field + (o.hint ? '
    ' + o.hint + "
    " : "") + "
    "; +} + +function select(label, key, value, options, hint) { + return "
    " + (hint ? '
    ' + hint + "
    " : "") + "
    "; +} + +const sec = (t) => '
    ' + t + "
    "; + +function renderForm() { + const r = current(); + if (!r) { + $("form").innerHTML = '

    ' + (rows.length ? "Select a configuration." : "No GDB server configurations yet. Add one with +: an embedded board through OpenOCD, J-Link, ST-LINK…, or a gdbserver on another machine.") + "

    "; + return; + } + const emb = r.kind === "embedded"; + let h = '
    ' + + input("Name", "name", r.name) + + '
    ' + + '" + + '
    " + + input("Target", "target", r.target, { list: "targets", placeholder: "the active target (" + (activeTarget || "none") + ")", hint: "The CMake executable that's debugged" + (emb ? " and flashed" : "") + "; it's built first." }) + + input("Executable", "program", r.program, { placeholder: "the target's", hint: "Another file instead (an .elf built some other way)." }) + + input("Debugger", "debugger", r.debugger, { list: "debuggers", placeholder: defaultDebugger, hint: "A gdb that knows the target's architecture." }) + + ""; + if (emb) { + h += sec("GDB server") + + '
    Common settings to start from; then set your board\'s config files or device.
    ' + + input("GDB server", "server", r.server, { placeholder: "none: it's already running", hint: "Started before gdb connects, and stopped after." }) + + input("Server arguments", "serverArgs", r.serverArgs, { area: true, placeholder: "-f interface/stlink.cfg -f target/stm32f4x.cfg" }) + + input("Ready when it prints", "serverReady", r.serverReady, { placeholder: "Listening on port 3333", hint: "A regular expression; empty: it gets a moment." }) + + input("Server environment", "serverEnvironment", r.serverEnvironment, { placeholder: "A=1;B=2" }) + + input("'target remote' args", "remote", r.remote, { placeholder: "localhost:3333" }) + + sec("Download") + + select("Download executable", "download", r.download, [["always", "Always"], ["updated", "If updated"], ["never", "Never"]], "Flash the program when debugging starts.") + + input("Reset command", "resetCommand", r.resetCommand, { placeholder: "monitor reset halt", hint: "Before the download, and again after it." }) + + input("Download & Run starts it with", "runCommands", r.runCommands, { area: true, placeholder: "monitor reset run", hint: "gdb commands, one per line, after the download (▶ Run, without a debugger)." }); + } else { + h += sec("Connection") + + input("'target remote' args", "remote", r.remote, { placeholder: r.ssh ? "its port: :2345" : "192.168.1.20:2345", hint: r.ssh ? "With SSH, the host is the SSH host's; only the port counts." : "Where gdbserver listens." }) + + input("Sysroot", "sysroot", r.sysroot, { placeholder: "none", hint: "The target's root, for its shared libraries." }) + + input("Path mappings", "pathMappings", r.pathMappings, { area: true, placeholder: "/remote/src=" + "$" + "{sourceDir}", hint: "Where its sources were built=where they are here, one per line." }) + + sec("Upload and start over SSH (optional)") + + input("SSH host", "ssh", r.ssh, { placeholder: "user@host", hint: "The program is copied there (scp) and gdbserver started on it; ▶ Run runs it there." }) + + input("Remote path", "remotePath", r.remotePath, { placeholder: "/tmp/" }) + + input("gdbserver", "gdbserver", r.gdbserver, { placeholder: "gdbserver" }) + + input("Program arguments", "args", r.args, { placeholder: "--verbose" }); + } + h += sec("Advanced") + + input("gdb commands before connecting", "initCommands", r.initCommands, { area: true, placeholder: "set architecture arm", hint: "One per line." }) + + input("gdb commands after connecting", "connectCommands", r.connectCommands, { area: true, placeholder: "monitor arm semihosting enable" }) + + "
    "; + $("form").innerHTML = h; +} + +function render() { + renderList(); + renderForm(); +} + +$("list").addEventListener("click", (e) => { + const li = e.target.closest("li[data-id]"); + if (!li) return; + sel = li.dataset.id; + render(); +}); + +$("form").addEventListener("input", (e) => { + const r = current(); + if (!r) return; + if (e.target.id === "preset") { + const p = PRESETS[e.target.value]; + if (!p) return; + for (const k of ["server", "serverArgs", "serverReady", "remote", "resetCommand", "runCommands"]) r[k] = p[k]; + if (/^(Embedded|New)/.test(r.name)) r.name = freeName(p.label); + setDirty(true); + render(); + return; + } + const k = e.target.dataset.k; + if (!k) return; + r[k] = e.target.type === "checkbox" ? e.target.checked : e.target.value; + setDirty(true); + if (k === "kind") render(); + else if (k === "name") renderList(); + else if (k === "ssh") renderForm(); +}); + +function addRow(from) { + const r = Object.assign({}, EMPTY, from || {}, { id: uid(), origName: undefined }); + r.name = freeName(from && from.name ? from.name : "Embedded"); + const at = current() ? rows.indexOf(current()) + 1 : rows.length; + rows.splice(at, 0, r); + sel = r.id; + setDirty(true); + render(); + const name = $("form").querySelector('[data-k="name"]'); + if (name) { name.focus(); name.select(); } +} + +$("add").onclick = () => addRow(); +$("copy").onclick = () => { + const r = current(); + if (r) addRow({ ...r, rest: { ...r.rest } }); +}; +$("remove").onclick = () => { + const r = current(); + if (!r) return; + const i = rows.indexOf(r); + rows.splice(i, 1); + sel = rows[Math.min(i, rows.length - 1)]?.id ?? null; + setDirty(true); + render(); +}; +function move(d) { + const r = current(); + const i = rows.indexOf(r); + if (!r || i + d < 0 || i + d >= rows.length) return; + rows.splice(i, 1); + rows.splice(i + d, 0, r); + setDirty(true); + renderList(); +} +$("up").onclick = () => move(-1); +$("down").onclick = () => move(1); + +function save(close) { + if (!dirty && close) return vscode.postMessage({ type: "cancel" }); + vscode.postMessage({ type: "save", rows, close }); +} +$("ok").onclick = () => save(true); +$("apply").onclick = () => save(false); +$("cancel").onclick = () => vscode.postMessage({ type: "cancel" }); + +window.addEventListener("message", (e) => { + const m = e.data; + if (m.type === "init" || m.type === "saved") { + const keep = m.type === "saved" && current() ? current().name.trim() : null; + rows = m.rows; active = m.active; defaultDebugger = m.defaultDebugger; activeTarget = m.activeTarget; + $("targets").innerHTML = m.targets.map((t) => '