debian-lsp-0.1.4/.cargo_vcs_info.json0000644000000001361046102023000130760ustar { "git": { "sha1": "fcd5ea81ab87ee222abe343af83941ef542b623a" }, "path_in_vcs": "" }debian-lsp-0.1.4/.github/CODEOWNERS000064400000000000000000000000121046102023000145700ustar 00000000000000* @jelmer debian-lsp-0.1.4/.github/FUNDING.yml000064400000000000000000000000171046102023000150170ustar 00000000000000github: jelmer debian-lsp-0.1.4/.github/dependabot.yml000064400000000000000000000007251046102023000160400ustar 00000000000000# Please see the documentation for all configuration options: # https://help.github.com/github/administering-a-repository/configuration-options-for-dependency-updates --- version: 2 updates: - package-ecosystem: "cargo" directory: "/" schedule: interval: "monthly" rebase-strategy: "disabled" groups: cargo: patterns: - "*" - package-ecosystem: "github-actions" directory: "/" schedule: interval: monthly debian-lsp-0.1.4/.github/workflows/auto-merge.yaml000064400000000000000000000011341046102023000201710ustar 00000000000000name: Dependabot auto-merge on: pull_request_target permissions: pull-requests: write contents: write jobs: dependabot: runs-on: ubuntu-latest if: ${{ github.actor == 'dependabot[bot]' }} steps: - name: Dependabot metadata id: metadata uses: dependabot/fetch-metadata@v2 with: github-token: "${{ secrets.GITHUB_TOKEN }}" - name: Enable auto-merge for Dependabot PRs run: gh pr merge --auto --squash "$PR_URL" env: PR_URL: ${{github.event.pull_request.html_url}} GITHUB_TOKEN: ${{secrets.GITHUB_TOKEN}} debian-lsp-0.1.4/.github/workflows/ci.yml000064400000000000000000000073701046102023000163660ustar 00000000000000--- name: CI "on": push: branches: - main pull_request: branches: - main env: CARGO_TERM_COLOR: always jobs: rust-check: name: Rust checks runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: components: rustfmt - name: Cache cargo registry uses: actions/cache@v5 with: path: ~/.cargo/registry key: ${{ runner.os }}-cargo-registry-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo index uses: actions/cache@v5 with: path: ~/.cargo/git key: ${{ runner.os }}-cargo-index-${{ hashFiles('**/Cargo.lock') }} - name: Cache cargo build uses: actions/cache@v5 with: path: target key: ${{ runner.os }}-cargo-build-target-${{ hashFiles('**/Cargo.lock') }} - name: Install base-files run: sudo apt-get update && sudo apt-get install -y base-files - name: Check formatting run: cargo fmt --all -- --check - name: Run tests run: cargo test --verbose - name: Build run: cargo build --release --verbose coc-debian-check: name: coc-debian checks runs-on: ubuntu-latest defaults: run: working-directory: ./coc-debian steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '18' cache: 'npm' cache-dependency-path: coc-debian/package-lock.json - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Run tests run: npm test continue-on-error: true # Since we don't have npm ci setup yet vscode-debian-check: name: vscode-debian checks runs-on: ubuntu-latest defaults: run: working-directory: ./vscode-debian steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' cache: 'npm' cache-dependency-path: vscode-debian/package-lock.json - name: Install dependencies run: npm ci - name: Lint run: npm run lint - name: Compile run: npm run compile - name: Package extension run: npm run package integration: name: Integration test runs-on: ubuntu-latest needs: [rust-check, coc-debian-check, vscode-debian-check] steps: - uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' - name: Build LSP server run: cargo build --release - name: Build coc plugin working-directory: ./coc-debian run: | npm install npm run build - name: Build vscode extension working-directory: ./vscode-debian run: | npm install npm run compile - name: Test LSP server binary run: | # Test that the binary runs and responds to basic LSP protocol echo '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"capabilities":{}}}' | timeout 5s ./target/release/debian-lsp || true # Check if binary exists and is executable test -x ./target/release/debian-lsp - name: Check integration run: | # Verify all components are built test -f ./target/release/debian-lsp test -f ./coc-debian/lib/index.js test -f ./coc-debian/package.json test -f ./vscode-debian/out/extension.js test -f ./vscode-debian/package.json debian-lsp-0.1.4/.github/workflows/publish-vscode.yaml000064400000000000000000000101111046102023000210460ustar 00000000000000--- name: Publish Extensions "on": push: branches: - main tags: - "v*" pull_request: branches: - main env: CARGO_TERM_COLOR: always jobs: build-lsp: name: Build LSP Server runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable - name: Install base-files run: sudo apt-get update && sudo apt-get install -y base-files - name: Build release binary run: cargo build --release --verbose - name: Upload LSP binary uses: actions/upload-artifact@v4 with: name: debian-lsp-binary path: target/release/debian-lsp publish-vscode: name: Publish VS Code Extension runs-on: ubuntu-latest needs: [build-lsp] defaults: run: working-directory: ./vscode-debian steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '20' cache: 'npm' cache-dependency-path: vscode-debian/package-lock.json - name: Install dependencies run: npm ci - name: Lint run: npm run lint - name: Compile run: npm run compile - name: Package extension run: npm run package - name: Upload VSIX artifact uses: actions/upload-artifact@v4 with: name: vscode-debian-vsix path: vscode-debian/*.vsix - name: Publish to Open VSX Registry if: startsWith(github.ref, 'refs/tags/v') && secrets.OPENVSX_TOKEN != '' run: | npm install -g ovsx ovsx publish -p ${{ secrets.OPENVSX_TOKEN }} continue-on-error: true - name: Publish to VS Code Marketplace if: startsWith(github.ref, 'refs/tags/v') && secrets.VSCE_TOKEN != '' run: | npx vsce publish -p ${{ secrets.VSCE_TOKEN }} continue-on-error: true publish-coc: name: Package coc-debian runs-on: ubuntu-latest needs: [build-lsp] defaults: run: working-directory: ./coc-debian steps: - uses: actions/checkout@v6 - name: Setup Node.js uses: actions/setup-node@v6 with: node-version: '18' cache: 'npm' cache-dependency-path: coc-debian/package-lock.json - name: Install dependencies run: npm ci - name: Build run: npm run build - name: Upload coc-debian artifact uses: actions/upload-artifact@v4 with: name: coc-debian path: | coc-debian/lib/ coc-debian/package.json coc-debian/README.md - name: Publish to npm if: startsWith(github.ref, 'refs/tags/v') && secrets.NPM_TOKEN != '' run: npm publish env: NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }} create-release: name: Create GitHub Release if: startsWith(github.ref, 'refs/tags/v') runs-on: ubuntu-latest needs: [build-lsp, publish-vscode, publish-coc] permissions: contents: write steps: - uses: actions/checkout@v6 - name: Download LSP binary uses: actions/download-artifact@v4 with: name: debian-lsp-binary path: ./artifacts/ - name: Download VSIX uses: actions/download-artifact@v4 with: name: vscode-debian-vsix path: ./artifacts/ - name: Download coc-debian uses: actions/download-artifact@v4 with: name: coc-debian path: ./artifacts/coc-debian/ - name: Create tarball for coc-debian run: | cd artifacts/coc-debian tar czf ../coc-debian.tar.gz . cd ../.. - name: Create Release uses: softprops/action-gh-release@v1 with: files: | artifacts/debian-lsp artifacts/*.vsix artifacts/coc-debian.tar.gz draft: false prerelease: false generate_release_notes: true env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} debian-lsp-0.1.4/.github/workflows/test.yml000064400000000000000000000015101046102023000167400ustar 00000000000000--- name: Tests "on": [push, pull_request] env: CARGO_TERM_COLOR: always jobs: test: name: Test Suite runs-on: ${{ matrix.os }} strategy: matrix: os: [ubuntu-latest, macos-latest, windows-latest] rust: [stable] steps: - uses: actions/checkout@v6 - name: Install Rust uses: dtolnay/rust-toolchain@stable with: toolchain: ${{ matrix.rust }} components: rustfmt - name: Install base-files (Linux only) run: sudo apt-get update && sudo apt-get install -y base-files if: matrix.os == 'ubuntu-latest' - name: Check formatting run: cargo fmt --all -- --check if: matrix.os == 'ubuntu-latest' - name: Run tests run: cargo test --verbose - name: Build run: cargo build --verbose debian-lsp-0.1.4/.gitignore000064400000000000000000000001731046102023000136350ustar 00000000000000# Rust /target Cargo.lock *~ # Node.js node_modules/ coc-debian/lib/ coc-debian/node_modules/ *.log # Editor *.swp *.swo debian-lsp-0.1.4/CONTRIBUTING.md000064400000000000000000000156621046102023000141070ustar 00000000000000# Contributing to debian-lsp Thank you for your interest in contributing to debian-lsp! This document provides debian-lsp-specific guidelines for contributing. For general pull request advice, see ## Performance Philosophy **Performance is critical for this project.** As a language server, responsiveness directly impacts the developer experience. We must ensure fast response times for all LSP operations. ### Incremental Processing is Key The project uses [Salsa](https://github.com/salsa-rs/salsa) for incremental computation. The fundamental principle is: **Process as little of the file as possible when something changes.** When a user types in their editor, we receive incremental change notifications. Our goal is to: 1. **Parse incrementally**: Only reparse the affected portion of the file 2. **Recompute minimally**: Only update computations that depend on the changed parts 3. **Cache aggressively**: Let Salsa cache intermediate results so unchanged queries return cached values ### Performance Guidelines for Contributors When adding features or fixing bugs: 1. **Use Salsa queries**: Structure your code as Salsa queries in `workspace.rs` so they benefit from automatic incrementality and caching. 2. **Minimize reparsing**: - Don't re-parse entire files on every change - Use the lossless parser (`deb822-lossless`) which preserves the parse tree structure - Only traverse the relevant parts of the syntax tree 3. **Avoid unnecessary work**: - If a change is in a comment, skip analysis that only cares about field values - If a change is in one paragraph of `debian/control`, don't reprocess other paragraphs - Check the changed range before deciding what to recompute 4. **Think about locality**: - Completions only need context around the cursor position - Diagnostics for one paragraph don't require analyzing others (in most cases) - Hover information only needs the element under the cursor 5. **Profile before optimizing**: If you suspect a performance issue, measure it. The LSP server logs can help identify slow operations. ### Example: Adding a New Diagnostic ```rust // BAD: Parses the whole file every time fn get_diagnostics(uri: &Uri, text: &str) -> Vec { let file = parse_entire_file(text); // expensive! analyze_everything(file) } // GOOD: Uses Salsa query that caches parse result #[salsa::tracked] fn diagnostics(db: &dyn Db, file: File) -> Vec { let parsed = parse_file(db, file); // cached by Salsa analyze_relevant_parts(db, parsed) } ``` ## Development Setup ### Building ```bash cargo build --release ``` ### Running Tests ```bash cargo test ``` ### Code Quality Checks Before submitting a PR, ensure these pass: ```bash cargo fmt # Format code cargo clippy # Check for issues cargo test # Run tests ``` All CI checks must pass, including formatting, clippy, and tests. ## debian-lsp Specific Conventions ### File Type Detection File types are detected in `FileType::detect()` based on URI patterns. When adding support for a new file type: 1. Add the variant to `FileType` enum in `src/main.rs` 2. Add detection logic in `FileType::detect()` 3. Create a new module under `src/` for the file type 4. Wire up the handlers in the LSP server implementation ### Parser Integration We use parsers from the `deb822-lossless` family for most Debian file formats: - `debian-control` for control files - `debian-changelog` for changelog files - `debian-copyright` for copyright files (with `lossless` feature) - `debian-watch` for watch files - `deb822-lossless` for generic deb822 files (e.g. `debian/tests/control`) These parsers preserve all whitespace and formatting, enabling: - Precise error locations - Format-preserving edits (quick fixes) - Incremental reparsing ### Error Tolerance **Parsers should be tolerant to errors and parse as much as possible.** When encountering malformed input, parsers should: 1. **Continue parsing**: Don't abort on the first error. Parse as much of the file as possible to provide maximum utility to the user. 2. **Record diagnostics**: Collect error information (invalid syntax, unexpected tokens, etc.) but keep processing subsequent content. 3. **Recover gracefully**: When encountering invalid structure, make reasonable assumptions to continue: - Skip invalid fields but continue with the next field - Parse the next paragraph even if the current one is malformed - Use partial/incomplete parse nodes rather than failing completely 4. **Provide useful information**: Even with errors, the LSP should offer: - Completions for valid parts of the file - Hover information on successfully parsed elements - Diagnostics that identify all issues, not just the first one **Why this matters**: Users edit files incrementally. At any moment, the file may be syntactically invalid (missing closing quote, incomplete field, etc.). The language server must remain functional during these transient invalid states to provide a good editing experience. Example: If `debian/control` has a malformed field in the Source paragraph, we should still parse and provide features for the binary package paragraphs that follow. ### Workspace Management The `Workspace` struct uses Salsa for incremental computation. When adding new LSP features: 1. Add Salsa queries to `workspace.rs` for your computation 2. Use `File` as the input to your queries (represents a document) 3. Let Salsa track dependencies automatically ### Testing Tests should verify both functionality and error cases. For diagnostics and quick fixes: - Test that diagnostics are produced at the correct positions - Test that quick fixes produce the expected edits - Test edge cases (empty files, malformed input, etc.) Use `assert_eq!` for exact matching rather than partial checks like `.contains()`. ## Areas for Contribution - **New file format support**: `debian/upstream/metadata`, `debian/gbp.conf`, etc. - **Enhanced diagnostics**: Detect more packaging issues - **Quick fixes**: Automatic corrections for common problems - **Semantic analysis**: Cross-file checking (e.g., package name consistency) - **Performance optimization**: Identify and fix bottlenecks - **Editor integration**: Improve coc-debian and vscode-debian plugins ## Architecture Notes ``` main.rs # LSP server implementation, message routing workspace.rs # Salsa database and incremental queries control/ # debian/control support copyright/ # debian/copyright support watch/ # debian/watch support changelog/ # debian/changelog support deb822/ # Common deb822 format utilities position.rs # LSP <-> text position conversions ``` The LSP server in `main.rs` handles protocol messages and delegates to file-type-specific modules. The `Workspace` provides incremental computation infrastructure. ## Questions For questions or bug reports, open an issue on GitHub: debian-lsp-0.1.4/Cargo.lock0000644000001346011046102023000110560ustar # This file is automatically @generated by Cargo. # It is not intended for manual editing. version = 4 [[package]] name = "addr2line" version = "0.25.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1b5d307320b3181d6d7954e663bd7c774a838b8220fe0593c86d9fb09f498b4b" dependencies = [ "gimli", ] [[package]] name = "adler2" version = "2.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "320119579fcad9c21884f5c4861d16174d0e06250625266f50fe6898340abefa" [[package]] name = "aho-corasick" version = "1.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ddd31a130427c27518df266943a5308ed92d4b226cc639f5a8f1002816174301" dependencies = [ "memchr", ] [[package]] name = "allocator-api2" version = "0.2.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "683d7910e743518b0e34f1186f92494becacb047c7b6bf616c96772180fef923" [[package]] name = "android_system_properties" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "819e7219dbd41043ac279b19830f2efc897156490d7fd6ea916720117ee66311" dependencies = [ "libc", ] [[package]] name = "anstream" version = "0.6.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "43d5b281e737544384e969a5ccad3f1cdd24b48086a0fc1b2a5262a26b8f4f4a" dependencies = [ "anstyle", "anstyle-parse", "anstyle-query", "anstyle-wincon", "colorchoice", "is_terminal_polyfill", "utf8parse", ] [[package]] name = "anstyle" version = "1.0.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5192cca8006f1fd4f7237516f40fa183bb07f8fbdfedaa0036de5ea9b0b45e78" [[package]] name = "anstyle-parse" version = "0.2.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4e7644824f0aa2c7b9384579234ef10eb7efb6a0deb83f9630a49594dd9c15c2" dependencies = [ "utf8parse", ] [[package]] name = "anstyle-query" version = "1.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "40c48f72fd53cd289104fc64099abca73db4166ad86ea0b4341abe65af83dadc" dependencies = [ "windows-sys", ] [[package]] name = "anstyle-wincon" version = "3.0.11" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "291e6a250ff86cd4a820112fb8898808a366d8f9f58ce16d1f538353ad55747d" dependencies = [ "anstyle", "once_cell_polyfill", "windows-sys", ] [[package]] name = "async-trait" version = "0.1.89" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9035ad2d096bed7955a320ee7e2230574d28fd3c3a0f186cbea1ff3c7eed5dbb" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "autocfg" version = "1.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c08606f8c3cbf4ce6ec8e28fb0014a2c086708fe954eaa885384a6165172e7e8" [[package]] name = "backtrace" version = "0.3.76" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bb531853791a215d7c62a30daf0dde835f381ab5de4589cfe7c649d2cbe92bd6" dependencies = [ "addr2line", "cfg-if", "libc", "miniz_oxide", "object", "rustc-demangle", "windows-link", ] [[package]] name = "base64" version = "0.22.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b3254f16251a8381aa12e40e3c4d2f0199f8c6508fbecb9d91f575e0fbb8c6" [[package]] name = "bitflags" version = "2.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "843867be96c8daad0d758b57df9392b6d8d271134fce549de6ce169ff98a92af" [[package]] name = "borrow-or-share" version = "0.2.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc0b364ead1874514c8c2855ab558056ebfeb775653e7ae45ff72f28f8f3166c" [[package]] name = "boxcar" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "36f64beae40a84da1b4b26ff2761a5b895c12adc41dc25aaee1c4f2bbfe97a6e" [[package]] name = "bumpalo" version = "3.20.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d20789868f4b01b2f2caec9f5c4e0213b41e3e5702a50157d699ae31ced2fcb" [[package]] name = "bytes" version = "1.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1e748733b7cbc798e1434b6ac524f0c1ff2ab456fe201501e6497c8417a4fc33" [[package]] name = "cc" version = "1.2.56" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aebf35691d1bfb0ac386a69bac2fde4dd276fb618cf8bf4f5318fe285e821bb2" dependencies = [ "find-msvc-tools", "shlex", ] [[package]] name = "cfg-if" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9330f8b2ff13f34540b44e946ef35111825727b38d33286ef986142615121801" [[package]] name = "chrono" version = "0.4.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c673075a2e0e5f4a1dde27ce9dee1ea4558c7ffe648f576438a20ca1d2acc4b0" dependencies = [ "iana-time-zone", "js-sys", "num-traits", "wasm-bindgen", "windows-link", ] [[package]] name = "clap" version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2797f34da339ce31042b27d23607e051786132987f595b02ba4f6a6dffb7030a" dependencies = [ "clap_builder", "clap_derive", ] [[package]] name = "clap_builder" version = "4.5.60" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "24a241312cea5059b13574bb9b3861cabf758b879c15190b37b6d6fd63ab6876" dependencies = [ "anstream", "anstyle", "clap_lex", "strsim", ] [[package]] name = "clap_derive" version = "4.5.55" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a92793da1a46a5f2a02a6f4c46c6496b28c43638adea8306fcb0caa1634f24e5" dependencies = [ "heck", "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "clap_lex" version = "1.0.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a822ea5bc7590f9d40f1ba12c0dc3c2760f3482c6984db1573ad11031420831" [[package]] name = "colorchoice" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b05b61dc5112cbb17e4b6cd61790d9845d13888356391624cbe7e41efeac1e75" [[package]] name = "core-foundation-sys" version = "0.8.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "773648b94d0e5d620f64f280777445740e61fe701025087ec8b57f45c791888b" [[package]] name = "countme" version = "3.0.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7704b5fdd17b18ae31c4c1da5a2e0305a2bf17b5249300a9ee9ed7b72114c636" [[package]] name = "crossbeam-deque" version = "0.8.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9dd111b7b7f7d55b72c0a6ae361660ee5853c9af73f70c3c2ef6858b950e2e51" dependencies = [ "crossbeam-epoch", "crossbeam-utils", ] [[package]] name = "crossbeam-epoch" version = "0.9.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5b82ac4a3c2ca9c3460964f020e1402edd5753411d7737aa39c3714ad1b5420e" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-queue" version = "0.3.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0f58bbc28f91df819d0aa2a2c00cd19754769c2fad90579b3592b1c9ba7a3115" dependencies = [ "crossbeam-utils", ] [[package]] name = "crossbeam-utils" version = "0.8.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d0a5c400df2834b80a4c3327b3aad3a4c4cd4de0629063962b03235697506a28" [[package]] name = "csv" version = "1.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "52cd9d68cf7efc6ddfaaee42e7288d3a99d613d4b50f76ce9827ae0c6e14f938" dependencies = [ "csv-core", "itoa", "ryu", "serde_core", ] [[package]] name = "csv-core" version = "0.1.13" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "704a3c26996a80471189265814dbc2c257598b96b8a7feae2d31ace646bb9782" dependencies = [ "memchr", ] [[package]] name = "dashmap" version = "6.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5041cc499144891f3790297212f32a74fb938e5136a14943f338ef9e0ae276cf" dependencies = [ "cfg-if", "crossbeam-utils", "hashbrown 0.14.5", "lock_api", "once_cell", "parking_lot_core", ] [[package]] name = "deb822-derive" version = "0.3.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e83ef29a094bcb2b7dd0f609ace7f5a34ef9a62e0731ebd350637640320a3b15" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "deb822-fast" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "114c474fa4cd5d6d24bb5e68b36fa4ef70f5b830e3cc14a9b66a12e71a15aeb9" dependencies = [ "deb822-derive", ] [[package]] name = "deb822-lossless" version = "0.5.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "84462549961d1b612f244697d68105f6e5c7ed640f7ead092592a31e2ac1af8a" dependencies = [ "regex", "rowan", "serde", ] [[package]] name = "debian-changelog" version = "0.2.15" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c54dc825b416cb3e80a9dbde13ae400ab8b8a7388bac6dc2ef4a159d0396b043" dependencies = [ "chrono", "debversion", "lazy-regex", "log", "rowan", "textwrap", "whoami", ] [[package]] name = "debian-control" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "64976e0f7ee9c04a0318f977d49b9ddec053d04abccda286994d9f2f65bfb36a" dependencies = [ "chrono", "deb822-fast", "deb822-lossless", "debversion", "regex", "rowan", "url", ] [[package]] name = "debian-copyright" version = "0.1.43" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "54bf50d7620f0de2fc7bd418b6f362a8fc573481c1b29916170d70c8e87f5d6c" dependencies = [ "deb822-fast", "deb822-lossless", "debversion", "regex", ] [[package]] name = "debian-lsp" version = "0.1.4" dependencies = [ "async-trait", "chrono", "deb822-fast", "deb822-lossless", "debian-changelog", "debian-control", "debian-copyright", "debian-watch", "distro-info", "rowan", "salsa", "serde", "serde_json", "text-size", "tokio", "tokio-test", "tower", "tower-lsp-server", "tower-service", "yaml-edit", ] [[package]] name = "debian-watch" version = "0.4.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "71155bfc78e4ad948d99bad6a99be8ef022abca1919f1888065cc8a6a1b6d6f3" dependencies = [ "clap", "deb822-lossless", "debversion", "m_lexer", "regex", "rowan", "url", ] [[package]] name = "debversion" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8ba0e270fb9f27dbb4c46e08d2ad27e69501d6ca573bfdf9e0aa793e7377929" dependencies = [ "chrono", "lazy-regex", "num-bigint", ] [[package]] name = "displaydoc" version = "0.2.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "97369cbbc041bc366949bc74d34658d6cda5621039731c6310521892a3a20ae0" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "distro-info" version = "0.4.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ef12237f2ced990e453ec0b69230752e73be0a357817448c50a62f8bbbe0ca71" dependencies = [ "chrono", "csv", "failure", ] [[package]] name = "either" version = "1.15.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "48c757948c5ede0e46177b7add2e67155f70e33c07fea8284df6576da70b3719" [[package]] name = "equivalent" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "877a4ace8713b0bcf2a4e7eec82529c029f1d0619886d18145fea96c3ffe5c0f" [[package]] name = "errno" version = "0.3.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "39cab71617ae0d63f51a36d69f866391735b51691dbda63cf6f96d042b63efeb" dependencies = [ "libc", "windows-sys", ] [[package]] name = "failure" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d32e9bd16cc02eae7db7ef620b392808b89f6a5e16bb3497d159c6b92a0f4f86" dependencies = [ "backtrace", "failure_derive", ] [[package]] name = "failure_derive" version = "0.1.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "aa4da3c766cd7a0db8242e326e9e4e081edd567072893ed320008189715366a4" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", "synstructure 0.12.6", ] [[package]] name = "find-msvc-tools" version = "0.1.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5baebc0774151f905a1a2cc41989300b1e6fbb29aff0ceffa1064fdd3088d582" [[package]] name = "fluent-uri" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "bc74ac4d8359ae70623506d512209619e5cf8f347124910440dbc221714b328e" dependencies = [ "borrow-or-share", "ref-cast", "serde", ] [[package]] name = "foldhash" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d9c4f5dac5e15c24eb999c26181a6ca40b39fe946cbe4c263c7209467bc83af2" [[package]] name = "form_urlencoded" version = "1.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cb4cb245038516f5f85277875cdaa4f7d2c9a0fa0468de06ed190163b1581fcf" dependencies = [ "percent-encoding", ] [[package]] name = "futures" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8b147ee9d1f6d097cef9ce628cd2ee62288d963e16fb287bd9286455b241382d" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-sink", "futures-task", "futures-util", ] [[package]] name = "futures-channel" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "07bbe89c50d7a535e539b8c17bc0b49bdb77747034daa8087407d655f3f7cc1d" dependencies = [ "futures-core", "futures-sink", ] [[package]] name = "futures-core" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7e3450815272ef58cec6d564423f6e755e25379b217b0bc688e295ba24df6b1d" [[package]] name = "futures-io" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "cecba35d7ad927e23624b22ad55235f2239cfa44fd10428eecbeba6d6a717718" [[package]] name = "futures-macro" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e835b70203e41293343137df5c0664546da5745f82ec9b84d40be8336958447b" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "futures-sink" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c39754e157331b013978ec91992bde1ac089843443c49cbc7f46150b0fad0893" [[package]] name = "futures-task" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "037711b3d59c33004d3856fbdc83b99d4ff37a24768fa1be9ce3538a1cde4393" [[package]] name = "futures-util" version = "0.3.32" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "389ca41296e6190b48053de0321d02a77f32f8a5d2461dd38762c0593805c6d6" dependencies = [ "futures-channel", "futures-core", "futures-io", "futures-macro", "futures-sink", "futures-task", "memchr", "pin-project-lite", "slab", ] [[package]] name = "gimli" version = "0.32.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e629b9b98ef3dd8afe6ca2bd0f89306cec16d43d907889945bc5d6687f2f13c7" [[package]] name = "hashbrown" version = "0.14.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e5274423e17b7c9fc20b6e7e208532f9b19825d82dfd615708b70edd83df41f1" [[package]] name = "hashbrown" version = "0.15.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9229cfe53dfd69f0609a49f65461bd93001ea1ef889cd5529dd176593f5338a1" dependencies = [ "allocator-api2", "equivalent", "foldhash", ] [[package]] name = "hashbrown" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "841d1cc9bed7f9236f321df977030373f4a4163ae1a7dbfe1a51a2c1a51d9100" [[package]] name = "hashlink" version = "0.10.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7382cf6263419f2d8df38c55d7da83da5c18aef87fc7a7fc1fb1e344edfe14c1" dependencies = [ "hashbrown 0.15.5", ] [[package]] name = "heck" version = "0.5.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2304e00983f87ffb38b55b444b5e3b60a884b5d30c0fca7d82fe33449bbe55ea" [[package]] name = "httparse" version = "1.10.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6dbf3de79e51f3d586ab4cb9d5c3e2c14aa28ed23d180cf89b4df0454a69cc87" [[package]] name = "iana-time-zone" version = "0.1.65" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e31bc9ad994ba00e440a8aa5c9ef0ec67d5cb5e5cb0cc7f8b744a35b389cc470" dependencies = [ "android_system_properties", "core-foundation-sys", "iana-time-zone-haiku", "js-sys", "log", "wasm-bindgen", "windows-core", ] [[package]] name = "iana-time-zone-haiku" version = "0.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f31827a206f56af32e590ba56d5d2d085f558508192593743f16b2306495269f" dependencies = [ "cc", ] [[package]] name = "icu_collections" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4c6b649701667bbe825c3b7e6388cb521c23d88644678e83c0c4d0a621a34b43" dependencies = [ "displaydoc", "potential_utf", "yoke", "zerofrom", "zerovec", ] [[package]] name = "icu_locale_core" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "edba7861004dd3714265b4db54a3c390e880ab658fec5f7db895fae2046b5bb6" dependencies = [ "displaydoc", "litemap", "tinystr", "writeable", "zerovec", ] [[package]] name = "icu_normalizer" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5f6c8828b67bf8908d82127b2054ea1b4427ff0230ee9141c54251934ab1b599" dependencies = [ "icu_collections", "icu_normalizer_data", "icu_properties", "icu_provider", "smallvec", "zerovec", ] [[package]] name = "icu_normalizer_data" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7aedcccd01fc5fe81e6b489c15b247b8b0690feb23304303a9e560f37efc560a" [[package]] name = "icu_properties" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "020bfc02fe870ec3a66d93e677ccca0562506e5872c650f893269e08615d74ec" dependencies = [ "icu_collections", "icu_locale_core", "icu_properties_data", "icu_provider", "zerotrie", "zerovec", ] [[package]] name = "icu_properties_data" version = "2.1.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "616c294cf8d725c6afcd8f55abc17c56464ef6211f9ed59cccffe534129c77af" [[package]] name = "icu_provider" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "85962cf0ce02e1e0a629cc34e7ca3e373ce20dda4c4d7294bbd0bf1fdb59e614" dependencies = [ "displaydoc", "icu_locale_core", "writeable", "yoke", "zerofrom", "zerotrie", "zerovec", ] [[package]] name = "idna" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b0875f23caa03898994f6ddc501886a45c7d3d62d04d2d90788d47be1b1e4de" dependencies = [ "idna_adapter", "smallvec", "utf8_iter", ] [[package]] name = "idna_adapter" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3acae9609540aa318d1bc588455225fb2085b9ed0c4f6bd0d9d5bcd86f1a0344" dependencies = [ "icu_normalizer", "icu_properties", ] [[package]] name = "indexmap" version = "2.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7714e70437a7dc3ac8eb7e6f8df75fd8eb422675fc7678aff7364301092b1017" dependencies = [ "equivalent", "hashbrown 0.16.1", ] [[package]] name = "intrusive-collections" version = "0.9.7" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "189d0897e4cbe8c75efedf3502c18c887b05046e59d28404d4d8e46cbc4d1e86" dependencies = [ "memoffset", ] [[package]] name = "inventory" version = "0.3.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "009ae045c87e7082cb72dab0ccd01ae075dd00141ddc108f43a0ea150a9e7227" dependencies = [ "rustversion", ] [[package]] name = "is_terminal_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a6cb138bb79a146c1bd460005623e142ef0181e3d0219cb493e02f7d08a35695" [[package]] name = "itoa" version = "1.0.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "92ecc6618181def0457392ccd0ee51198e065e016d1d527a7ac1b6dc7c1f09d2" [[package]] name = "js-sys" version = "0.3.91" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b49715b7073f385ba4bc528e5747d02e66cb39c6146efb66b781f131f0fb399c" dependencies = [ "once_cell", "wasm-bindgen", ] [[package]] name = "lazy-regex" version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6bae91019476d3ec7147de9aa291cadb6d870abf2f3015d2da73a90325ac1496" dependencies = [ "lazy-regex-proc_macros", "once_cell", "regex", ] [[package]] name = "lazy-regex-proc_macros" version = "3.6.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "4de9c1e1439d8b7b3061b2d209809f447ca33241733d9a3c01eabf2dc8d94358" dependencies = [ "proc-macro2", "quote", "regex", "syn 2.0.117", ] [[package]] name = "libc" version = "0.2.183" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b5b646652bf6661599e1da8901b3b9522896f01e736bad5f723fe7a3a27f899d" [[package]] name = "libredox" version = "0.1.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1744e39d1d6a9948f4f388969627434e31128196de472883b39f148769bfe30a" dependencies = [ "bitflags", "libc", "plain", "redox_syscall 0.7.3", ] [[package]] name = "litemap" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6373607a59f0be73a39b6fe456b8192fcc3585f602af20751600e974dd455e77" [[package]] name = "lock_api" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "224399e74b87b5f3557511d98dff8b14089b3dadafcab6bb93eab67d3aace965" dependencies = [ "scopeguard", ] [[package]] name = "log" version = "0.4.29" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5e5032e24019045c762d3c0f28f5b6b8bbf38563a65908389bf7978758920897" [[package]] name = "ls-types" version = "0.0.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "896e16b8e17d8732b9efe4d5b66cb0cc162b3023a2d8122f2aea6f7f185e0a67" dependencies = [ "bitflags", "fluent-uri", "percent-encoding", "serde", "serde_json", ] [[package]] name = "m_lexer" version = "0.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a7e51ebf91162d585a5bae05e4779efc4a276171cb880d61dd6fab11c98467a7" dependencies = [ "regex", ] [[package]] name = "memchr" version = "2.8.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f8ca58f447f06ed17d5fc4043ce1b10dd205e060fb3ce5b979b8ed8e59ff3f79" [[package]] name = "memoffset" version = "0.9.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "488016bfae457b036d996092f6cb448677611ce4449e970ceaf42695203f218a" dependencies = [ "autocfg", ] [[package]] name = "miniz_oxide" version = "0.8.9" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "1fa76a2c86f704bdb222d66965fb3d63269ce38518b83cb0575fca855ebb6316" dependencies = [ "adler2", ] [[package]] name = "mio" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a69bcab0ad47271a0234d9422b131806bf3968021e5dc9328caf2d4cd58557fc" dependencies = [ "libc", "wasi", "windows-sys", ] [[package]] name = "num-bigint" version = "0.4.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a5e44f723f1133c9deac646763579fdb3ac745e418f2a7af9cd0c431da1f20b9" dependencies = [ "num-integer", "num-traits", ] [[package]] name = "num-integer" version = "0.1.46" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7969661fd2958a5cb096e56c8e1ad0444ac2bbcd0061bd28660485a44879858f" dependencies = [ "num-traits", ] [[package]] name = "num-traits" version = "0.2.19" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "071dfc062690e90b734c0b2273ce72ad0ffa95f0c74596bc250dcfd960262841" dependencies = [ "autocfg", ] [[package]] name = "object" version = "0.37.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff76201f031d8863c38aa7f905eca4f53abbfa15f609db4277d44cd8938f33fe" dependencies = [ "memchr", ] [[package]] name = "once_cell" version = "1.21.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42f5e15c9953c5e4ccceeb2e7382a716482c34515315f7b03532b8b4e8393d2d" [[package]] name = "once_cell_polyfill" version = "1.70.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "384b8ab6d37215f3c5301a95a4accb5d64aa607f1fcb26a11b5303878451b4fe" [[package]] name = "parking_lot" version = "0.12.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "93857453250e3077bd71ff98b6a65ea6621a19bb0f559a85248955ac12c45a1a" dependencies = [ "lock_api", "parking_lot_core", ] [[package]] name = "parking_lot_core" version = "0.9.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2621685985a2ebf1c516881c026032ac7deafcda1a2c9b7850dc81e3dfcb64c1" dependencies = [ "cfg-if", "libc", "redox_syscall 0.5.18", "smallvec", "windows-link", ] [[package]] name = "percent-encoding" version = "2.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9b4f627cb1b25917193a259e49bdad08f671f8d9708acfd5fe0a8c1455d87220" [[package]] name = "pin-project-lite" version = "0.2.17" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "a89322df9ebe1c1578d689c92318e070967d1042b512afbe49518723f4e6d5cd" [[package]] name = "plain" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4596b6d070b27117e987119b4dac604f3c58cfb0b191112e24771b2faeac1a6" [[package]] name = "portable-atomic" version = "1.13.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c33a9471896f1c69cecef8d20cbe2f7accd12527ce60845ff44c153bb2a21b49" [[package]] name = "potential_utf" version = "0.1.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b73949432f5e2a09657003c25bca5e19a0e9c84f8058ca374f49e0ebe605af77" dependencies = [ "zerovec", ] [[package]] name = "proc-macro2" version = "1.0.106" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8fd00f0bb2e90d81d1044c2b32617f68fcb9fa3bb7640c23e9c748e53fb30934" dependencies = [ "unicode-ident", ] [[package]] name = "quote" version = "1.0.45" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41f2619966050689382d2b44f664f4bc593e129785a36d6ee376ddf37259b924" dependencies = [ "proc-macro2", ] [[package]] name = "rayon" version = "1.11.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "368f01d005bf8fd9b1206fb6fa653e6c4a81ceb1466406b81792d87c5677a58f" dependencies = [ "either", "rayon-core", ] [[package]] name = "rayon-core" version = "1.13.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "22e18b0f0062d30d4230b2e85ff77fdfe4326feb054b9783a3460d8435c8ab91" dependencies = [ "crossbeam-deque", "crossbeam-utils", ] [[package]] name = "redox_syscall" version = "0.5.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ed2bf2547551a7053d6fdfafda3f938979645c44812fbfcda098faae3f1a362d" dependencies = [ "bitflags", ] [[package]] name = "redox_syscall" version = "0.7.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce70a74e890531977d37e532c34d45e9055d2409ed08ddba14529471ed0be16" dependencies = [ "bitflags", ] [[package]] name = "ref-cast" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f354300ae66f76f1c85c5f84693f0ce81d747e2c3f21a45fef496d89c960bf7d" dependencies = [ "ref-cast-impl", ] [[package]] name = "ref-cast-impl" version = "1.0.25" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7186006dcb21920990093f30e3dea63b7d6e977bf1256be20c3563a5db070da" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "regex" version = "1.12.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e10754a14b9137dd7b1e3e5b0493cc9171fdd105e0ab477f51b72e7f3ac0e276" dependencies = [ "aho-corasick", "memchr", "regex-automata", "regex-syntax", ] [[package]] name = "regex-automata" version = "0.4.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6e1dd4122fc1595e8162618945476892eefca7b88c52820e74af6262213cae8f" dependencies = [ "aho-corasick", "memchr", "regex-syntax", ] [[package]] name = "regex-syntax" version = "0.8.10" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "dc897dd8d9e8bd1ed8cdad82b5966c3e0ecae09fb1907d58efaa013543185d0a" [[package]] name = "rowan" version = "0.16.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "417a3a9f582e349834051b8a10c8d71ca88da4211e4093528e36b9845f6b5f21" dependencies = [ "countme", "hashbrown 0.14.5", "rustc-hash 1.1.0", "text-size", ] [[package]] name = "rustc-demangle" version = "0.1.27" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b50b8869d9fc858ce7266cce0194bd74df58b9d0e3f6df3a9fc8eb470d95c09d" [[package]] name = "rustc-hash" version = "1.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "08d43f7aa6b08d49f382cde6a7982047c3426db949b1424bc4b7ec9ae12c6ce2" [[package]] name = "rustc-hash" version = "2.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "357703d41365b4b27c590e3ed91eabb1b663f07c4c084095e60cbed4362dff0d" [[package]] name = "rustversion" version = "1.0.22" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b39cdef0fa800fc44525c84ccb54a029961a8215f9619753635a9c0d2538d46d" [[package]] name = "ryu" version = "1.0.23" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9774ba4a74de5f7b1c1451ed6cd5285a32eddb5cccb8cc655a4e50009e06477f" [[package]] name = "salsa" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f77debccd43ba198e9cee23efd7f10330ff445e46a98a2b107fed9094a1ee676" dependencies = [ "boxcar", "crossbeam-queue", "crossbeam-utils", "hashbrown 0.15.5", "hashlink", "indexmap", "intrusive-collections", "inventory", "parking_lot", "portable-atomic", "rayon", "rustc-hash 2.1.1", "salsa-macro-rules", "salsa-macros", "smallvec", "thin-vec", "tracing", ] [[package]] name = "salsa-macro-rules" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ea07adbf42d91cc076b7daf3b38bc8168c19eb362c665964118a89bc55ef19a5" [[package]] name = "salsa-macros" version = "0.26.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d16d4d8b66451b9c75ddf740b7fc8399bc7b8ba33e854a5d7526d18708f67b05" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", "synstructure 0.13.2", ] [[package]] name = "scopeguard" version = "1.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "94143f37725109f92c262ed2cf5e59bce7498c01bcc1502d7b9afe439a4e9f49" [[package]] name = "serde" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9a8e94ea7f378bd32cbbd37198a4a91436180c5bb472411e48b5ec2e2124ae9e" dependencies = [ "serde_core", "serde_derive", ] [[package]] name = "serde_core" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "41d385c7d4ca58e59fc732af25c3983b67ac852c1a25000afe1175de458b67ad" dependencies = [ "serde_derive", ] [[package]] name = "serde_derive" version = "1.0.228" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d540f220d3187173da220f885ab66608367b6574e925011a9353e4badda91d79" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "serde_json" version = "1.0.149" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "83fc039473c5595ace860d8c4fafa220ff474b3fc6bfdb4293327f1a37e94d86" dependencies = [ "itoa", "memchr", "serde", "serde_core", "zmij", ] [[package]] name = "shlex" version = "1.3.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0fda2ff0d084019ba4d7c6f371c95d8fd75ce3524c3cb8fb653a3023f6323e64" [[package]] name = "signal-hook-registry" version = "1.4.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c4db69cba1110affc0e9f7bcd48bbf87b3f4fc7c61fc9155afd4c469eb3d6c1b" dependencies = [ "errno", "libc", ] [[package]] name = "slab" version = "0.4.12" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0c790de23124f9ab44544d7ac05d60440adc586479ce501c1d6d7da3cd8c9cf5" [[package]] name = "smallvec" version = "1.15.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "67b1b7a3b5fe4f1376887184045fcf45c69e92af734b7aaddc05fb777b6fbd03" [[package]] name = "smawk" version = "0.3.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b7c388c1b5e93756d0c740965c41e8822f866621d41acbdf6336a6a168f8840c" [[package]] name = "socket2" version = "0.6.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3a766e1110788c36f4fa1c2b71b387a7815aa65f88ce0229841826633d93723e" dependencies = [ "libc", "windows-sys", ] [[package]] name = "stable_deref_trait" version = "1.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6ce2be8dc25455e1f91df71bfa12ad37d7af1092ae736f3a6cd0e37bc7810596" [[package]] name = "strsim" version = "0.11.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7da8b5736845d9f2fcb837ea5d9e2628564b3b043a70948a3f0b778838c5fb4f" [[package]] name = "syn" version = "1.0.109" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72b64191b275b66ffe2469e8af2c1cfe3bafa67b529ead792a6d0160888b4237" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "syn" version = "2.0.117" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e665b8803e7b1d2a727f4023456bbbbe74da67099c585258af0ad9c5013b9b99" dependencies = [ "proc-macro2", "quote", "unicode-ident", ] [[package]] name = "sync_wrapper" version = "1.0.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "0bf256ce5efdfa370213c1dabab5935a12e49f2c58d15e9eac2870d3b4f27263" [[package]] name = "synstructure" version = "0.12.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f36bdaa60a83aca3921b5259d5400cbf5e90fc51931376a9bd4a0eb79aa7210f" dependencies = [ "proc-macro2", "quote", "syn 1.0.109", "unicode-xid", ] [[package]] name = "synstructure" version = "0.13.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "728a70f3dbaf5bab7f0c4b1ac8d7ae5ea60a4b5549c8a5914361c99147a709d2" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "text-size" version = "1.1.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f18aa187839b2bdb1ad2fa35ead8c4c2976b64e4363c386d45ac0f7ee85c9233" [[package]] name = "textwrap" version = "0.16.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "c13547615a44dc9c452a8a534638acdf07120d4b6847c8178705da06306a3057" dependencies = [ "smawk", "unicode-linebreak", "unicode-width", ] [[package]] name = "thin-vec" version = "0.2.14" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "144f754d318415ac792f9d69fc87abbbfc043ce2ef041c60f16ad828f638717d" [[package]] name = "tinystr" version = "0.8.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "42d3e9c45c09de15d06dd8acf5f4e0e399e85927b7f00711024eb7ae10fa4869" dependencies = [ "displaydoc", "zerovec", ] [[package]] name = "tokio" version = "1.50.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "27ad5e34374e03cfffefc301becb44e9dc3c17584f414349ebe29ed26661822d" dependencies = [ "bytes", "libc", "mio", "parking_lot", "pin-project-lite", "signal-hook-registry", "socket2", "tokio-macros", "windows-sys", ] [[package]] name = "tokio-macros" version = "2.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5c55a2eff8b69ce66c84f85e1da1c233edc36ceb85a2058d11b0d6a3c7e7569c" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "tokio-stream" version = "0.1.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "32da49809aab5c3bc678af03902d4ccddea2a87d028d86392a4b1560c6906c70" dependencies = [ "futures-core", "pin-project-lite", "tokio", ] [[package]] name = "tokio-test" version = "0.4.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f6d24790a10a7af737693a3e8f1d03faef7e6ca0cc99aae5066f533766de545" dependencies = [ "futures-core", "tokio", "tokio-stream", ] [[package]] name = "tokio-util" version = "0.7.18" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9ae9cec805b01e8fc3fd2fe289f89149a9b66dd16786abd8b19cfa7b48cb0098" dependencies = [ "bytes", "futures-core", "futures-sink", "pin-project-lite", "tokio", ] [[package]] name = "tower" version = "0.5.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebe5ef63511595f1344e2d5cfa636d973292adc0eec1f0ad45fae9f0851ab1d4" dependencies = [ "futures-core", "futures-util", "pin-project-lite", "sync_wrapper", "tower-layer", "tower-service", ] [[package]] name = "tower-layer" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "121c2a6cda46980bb0fcd1647ffaf6cd3fc79a013de288782836f6df9c48780e" [[package]] name = "tower-lsp-server" version = "0.23.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2f0e711655c89181a6bc6a2cc348131fcd9680085f5b06b6af13427a393a6e72" dependencies = [ "bytes", "dashmap", "futures", "httparse", "ls-types", "memchr", "serde", "serde_json", "tokio", "tokio-util", "tower", "tracing", ] [[package]] name = "tower-service" version = "0.3.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "8df9b6e13f2d32c91b9bd719c00d1958837bc7dec474d94952798cc8e69eeec3" [[package]] name = "tracing" version = "0.1.44" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "63e71662fa4b2a2c3a26f570f037eb95bb1f85397f3cd8076caed2f026a6d100" dependencies = [ "pin-project-lite", "tracing-attributes", "tracing-core", ] [[package]] name = "tracing-attributes" version = "0.1.31" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7490cfa5ec963746568740651ac6781f701c9c5ea257c58e057f3ba8cf69e8da" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "tracing-core" version = "0.1.36" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "db97caf9d906fbde555dd62fa95ddba9eecfd14cb388e4f491a66d74cd5fb79a" dependencies = [ "once_cell", ] [[package]] name = "unicode-ident" version = "1.0.24" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "e6e4313cd5fcd3dad5cafa179702e2b244f760991f45397d14d4ebf38247da75" [[package]] name = "unicode-linebreak" version = "0.1.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3b09c83c3c29d37506a3e260c08c03743a6bb66a9cd432c6934ab501a190571f" [[package]] name = "unicode-width" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b4ac048d71ede7ee76d585517add45da530660ef4390e49b098733c6e897f254" [[package]] name = "unicode-xid" version = "0.2.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ebc1c04c71510c7f702b52b7c350734c9ff1295c464a03335b00bb84fc54f853" [[package]] name = "url" version = "2.5.8" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ff67a8a4397373c3ef660812acab3268222035010ab8680ec4215f38ba3d0eed" dependencies = [ "form_urlencoded", "idna", "percent-encoding", "serde", ] [[package]] name = "utf8_iter" version = "1.0.4" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b6c140620e7ffbb22c2dee59cafe6084a59b5ffc27a8859a5f0d494b5d52b6be" [[package]] name = "utf8parse" version = "0.2.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "06abde3611657adf66d383f00b093d7faecc7fa57071cce2578660c9f1010821" [[package]] name = "wasi" version = "0.11.1+wasi-snapshot-preview1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ccf3ec651a847eb01de73ccad15eb7d99f80485de043efb2f370cd654f4ea44b" [[package]] name = "wasite" version = "0.1.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8dad83b4f25e74f184f64c43b150b91efe7647395b42289f38e50566d82855b" [[package]] name = "wasm-bindgen" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6532f9a5c1ece3798cb1c2cfdba640b9b3ba884f5db45973a6f442510a87d38e" dependencies = [ "cfg-if", "once_cell", "rustversion", "wasm-bindgen-macro", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-macro" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "18a2d50fcf105fb33bb15f00e7a77b772945a2ee45dcf454961fd843e74c18e6" dependencies = [ "quote", "wasm-bindgen-macro-support", ] [[package]] name = "wasm-bindgen-macro-support" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "03ce4caeaac547cdf713d280eda22a730824dd11e6b8c3ca9e42247b25c631e3" dependencies = [ "bumpalo", "proc-macro2", "quote", "syn 2.0.117", "wasm-bindgen-shared", ] [[package]] name = "wasm-bindgen-shared" version = "0.2.114" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "75a326b8c223ee17883a4251907455a2431acc2791c98c26279376490c378c16" dependencies = [ "unicode-ident", ] [[package]] name = "whoami" version = "1.6.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "5d4a4db5077702ca3015d3d02d74974948aba2ad9e12ab7df718ee64ccd7e97d" dependencies = [ "libredox", "wasite", ] [[package]] name = "windows-core" version = "0.62.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8e83a14d34d0623b51dce9581199302a221863196a1dde71a7663a4c2be9deb" dependencies = [ "windows-implement", "windows-interface", "windows-link", "windows-result", "windows-strings", ] [[package]] name = "windows-implement" version = "0.60.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "053e2e040ab57b9dc951b72c264860db7eb3b0200ba345b4e4c3b14f67855ddf" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "windows-interface" version = "0.59.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3f316c4a2570ba26bbec722032c4099d8c8bc095efccdc15688708623367e358" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "windows-link" version = "0.2.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "f0805222e57f7521d6a62e36fa9163bc891acd422f971defe97d64e70d0a4fe5" [[package]] name = "windows-result" version = "0.4.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7781fa89eaf60850ac3d2da7af8e5242a5ea78d1a11c49bf2910bb5a73853eb5" dependencies = [ "windows-link", ] [[package]] name = "windows-strings" version = "0.5.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7837d08f69c77cf6b07689544538e017c1bfcf57e34b4c0ff58e6c2cd3b37091" dependencies = [ "windows-link", ] [[package]] name = "windows-sys" version = "0.61.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "ae137229bcbd6cdf0f7b80a31df61766145077ddf49416a728b02cb3921ff3fc" dependencies = [ "windows-link", ] [[package]] name = "writeable" version = "0.6.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9edde0db4769d2dc68579893f2306b26c6ecfbe0ef499b013d731b7b9247e0b9" [[package]] name = "yaml-edit" version = "0.2.0" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "9e54cf551905b4d2f3d0bb5ed2b5ebf095ce2757530037dccae0cef3887fa3c8" dependencies = [ "base64", "rowan", ] [[package]] name = "yoke" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "72d6e5c6afb84d73944e5cedb052c4680d5657337201555f9f2a16b7406d4954" dependencies = [ "stable_deref_trait", "yoke-derive", "zerofrom", ] [[package]] name = "yoke-derive" version = "0.8.1" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b659052874eb698efe5b9e8cf382204678a0086ebf46982b79d6ca3182927e5d" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", "synstructure 0.13.2", ] [[package]] name = "zerofrom" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "50cc42e0333e05660c3587f3bf9d0478688e15d870fab3346451ce7f8c9fbea5" dependencies = [ "zerofrom-derive", ] [[package]] name = "zerofrom-derive" version = "0.1.6" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "d71e5d6e06ab090c67b5e44993ec16b72dcbaabc526db883a360057678b48502" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", "synstructure 0.13.2", ] [[package]] name = "zerotrie" version = "0.2.3" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "2a59c17a5562d507e4b54960e8569ebee33bee890c70aa3fe7b97e85a9fd7851" dependencies = [ "displaydoc", "yoke", "zerofrom", ] [[package]] name = "zerovec" version = "0.11.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "6c28719294829477f525be0186d13efa9a3c602f7ec202ca9e353d310fb9a002" dependencies = [ "yoke", "zerofrom", "zerovec-derive", ] [[package]] name = "zerovec-derive" version = "0.11.2" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "eadce39539ca5cb3985590102671f2567e659fca9666581ad3411d59207951f3" dependencies = [ "proc-macro2", "quote", "syn 2.0.117", ] [[package]] name = "zmij" version = "1.0.21" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "b8848ee67ecc8aedbaf3e4122217aff892639231befc6a1b58d29fff4c2cabaa" debian-lsp-0.1.4/Cargo.toml0000644000000041301046102023000110720ustar # THIS FILE IS AUTOMATICALLY GENERATED BY CARGO # # When uploading crates to the registry Cargo will automatically # "normalize" Cargo.toml files for maximal compatibility # with all versions of Cargo and also rewrite `path` dependencies # to registry (e.g., crates.io) dependencies. # # If you are reading this file be aware that the original Cargo.toml # will likely look very different (and much more reasonable). # See Cargo.toml.orig for the original contents. [package] edition = "2021" name = "debian-lsp" version = "0.1.4" authors = ["Jelmer Vernooij "] build = false autolib = false autobins = false autoexamples = false autotests = false autobenches = false description = "Language Server Protocol implementation for Debian control files with field completion, diagnostics, and quickfixes" readme = "README.md" keywords = [ "lsp", "debian", "control", "language-server", ] categories = [ "development-tools", "text-editors", ] license = "MIT OR Apache-2.0" repository = "https://github.com/jelmer/debian-lsp" [[bin]] name = "debian-lsp" path = "src/main.rs" test = true [dependencies.async-trait] version = "0.1" [dependencies.chrono] version = "0.4" [dependencies.deb822-fast] version = "0.2" [dependencies.deb822-lossless] version = "0.5.12" [dependencies.debian-changelog] version = "0.2.15" [dependencies.debian-control] version = "0.3.3" [dependencies.debian-copyright] version = "0.1.43" features = ["lossless"] [dependencies.debian-watch] version = "0.4.4" features = [ "linebased", "deb822", ] [dependencies.distro-info] version = "0.4" [dependencies.rowan] version = "0.16.1" [dependencies.salsa] version = ">=0.23, <0.27" [dependencies.serde] version = "1" features = ["derive"] [dependencies.serde_json] version = "1" [dependencies.text-size] version = "1.1" [dependencies.tokio] version = "1" features = ["full"] [dependencies.tower-lsp-server] version = "0.23" [dependencies.yaml-edit] version = "0.2.0" [dev-dependencies.tokio-test] version = "0.4" [dev-dependencies.tower] version = "0.5" [dev-dependencies.tower-service] version = "0.3" debian-lsp-0.1.4/Cargo.toml.orig000064400000000000000000000022311046102023000145310ustar 00000000000000[package] name = "debian-lsp" version = "0.1.4" edition = "2021" authors = ["Jelmer Vernooij "] description = "Language Server Protocol implementation for Debian control files with field completion, diagnostics, and quickfixes" readme = "README.md" license = "MIT OR Apache-2.0" repository = "https://github.com/jelmer/debian-lsp" keywords = ["lsp", "debian", "control", "language-server"] categories = ["development-tools", "text-editors"] [dependencies] # debian-analyzer = "0.159.0" async-trait = "0.1" chrono = "0.4" debian-changelog = "0.2.15" debian-control = { version = "0.3.3" } debian-copyright = { version = "0.1.43", features = ["lossless"] } debian-watch = { version = "0.4.4", features = ["linebased", "deb822"] } distro-info = "0.4" rowan = "0.16.1" deb822-fast = "0.2" deb822-lossless = { version = "0.5.12" } yaml-edit = { version = "0.2.0" } tower-lsp-server = "0.23" tokio = { version = "1", features = ["full"] } serde = { version = "1", features = ["derive"] } serde_json = "1" salsa = ">=0.23, <0.27" text-size = "1.1" [dev-dependencies] tokio-test = "0.4" tower = "0.5" tower-service = "0.3" [[bin]] name = "debian-lsp" test = true debian-lsp-0.1.4/README.md000064400000000000000000000123101046102023000131200ustar 00000000000000# debian-lsp [![CI](https://github.com/jelmer/debian-lsp/actions/workflows/ci.yml/badge.svg)](https://github.com/jelmer/debian-lsp/actions/workflows/ci.yml) [![Tests](https://github.com/jelmer/debian-lsp/actions/workflows/test.yml/badge.svg)](https://github.com/jelmer/debian-lsp/actions/workflows/test.yml) Language Server Protocol implementation for Debian packaging files. ## Supported Files - `debian/control` - Package control files - `debian/copyright` - DEP-5 copyright files - `debian/watch` - Upstream watch files (v1-4 line-based and v5 deb822 formats) - `debian/changelog` - Package changelog files - `debian/source/format` - Source format declaration files - `debian/tests/control` - Autopkgtest control files (basic support) - `debian/upstream/metadata` - DEP-12 upstream metadata files ## Features ### Completions **debian/control:** - Field name completions for all standard source and binary package fields - Package name completions for relationship fields (Depends, Build-Depends, Recommends, etc.) using the system package cache - Value completions for Section (all Debian sections including area-qualified), Priority, and architecture fields **debian/copyright:** - Field name completions for header, files, and license paragraphs - Value completions for Format and License (from `/usr/share/common-licenses`) **debian/watch:** - Field name completions for watch file fields - Version number completions - Option value completions (compression, mode, pgpmode, searchmode, gitmode, gitexport, component) **debian/changelog:** - Distribution completions (unstable, stable, testing, experimental, UNRELEASED, plus release codenames) - Urgency level completions (low, medium, high, critical, emergency) **debian/source/format:** - Format value completions (3.0 (quilt), 3.0 (native), 3.0 (git), 1.0, etc.) **debian/upstream/metadata:** - Field name completions for all DEP-12 fields (Repository, Bug-Database, Contact, etc.) ### Diagnostics - Field casing validation (e.g. `source` instead of `Source`) - Parse error reporting with position information ### Code Actions - **Fix field casing** - automatically correct field names to canonical casing - **Wrap and sort** - wrap long fields to 79 characters and sort dependency lists (control and copyright files) - **Add changelog entry** - create a new changelog entry with incremented version, UNRELEASED distribution, and auto-populated maintainer - **Mark for upload** - replace UNRELEASED with the target distribution ### Semantic Highlighting Custom token types for syntax highlighting of Debian-specific constructs: - Control/copyright/watch/upstream-metadata files: field names, unknown fields, values, comments - Changelog files: package name, version, distribution, urgency, maintainer, timestamp ## Installation ### Building the LSP server ```bash cargo build --release ``` The binary will be available at `target/release/debian-lsp`. ### Using with VS Code A dedicated VS Code extension is available in the `vscode-debian` directory. See [vscode-debian/README.md](vscode-debian/README.md) for installation and configuration instructions. ### Using with Vim/Neovim #### coc.nvim A coc.nvim extension is available in the `coc-debian` directory. See [coc-debian/README.md](coc-debian/README.md) for installation and configuration instructions. #### ALE Source the provided configuration file in your `.vimrc` or `init.vim`: ```vim source /path/to/debian-lsp/ale-debian-lsp.vim ``` By default, the configuration will look for the `debian-lsp` executable in the same directory as the vim file. To use a custom path, set `g:debian_lsp_executable` before sourcing: ```vim let g:debian_lsp_executable = '/custom/path/to/debian-lsp' source /path/to/debian-lsp/ale-debian-lsp.vim ``` You can trigger code actions in ALE with `:ALECodeAction` when your cursor is on a diagnostic. #### Native Neovim LSP Add the following configuration to your Neovim config (init.lua): ```lua -- Configure debian-lsp vim.api.nvim_create_autocmd({'BufEnter', 'BufWinEnter'}, { pattern = { '*/debian/control', '*/debian/copyright', '*/debian/changelog', '*/debian/source/format', '*/debian/watch', '*/debian/tests/control', '*/debian/upstream/metadata', }, callback = function() vim.lsp.start({ name = 'debian-lsp', cmd = {vim.fn.expand('~/src/debian-lsp/target/release/debian-lsp')}, root_dir = vim.fn.getcwd(), }) end, }) ``` Or if you prefer using lspconfig: ```lua local lspconfig = require('lspconfig') local configs = require('lspconfig.configs') -- Define the debian-lsp configuration if not configs.debian_lsp then configs.debian_lsp = { default_config = { cmd = {vim.fn.expand('~/src/debian-lsp/target/release/debian-lsp')}, filetypes = { 'debcontrol', 'debcopyright', 'debchangelog', 'debsources', 'make', 'yaml', }, root_dir = lspconfig.util.root_pattern('debian', '.git'), settings = {}, }, } end -- Enable debian-lsp lspconfig.debian_lsp.setup{} ``` Note: Adjust the `cmd` path to match your installation location. ## Development To run the LSP in development mode: ```bash cargo run ``` To watch and rebuild the coc plugin: ```bash cd coc-debian npm run watch ``` debian-lsp-0.1.4/ale-debian-lsp.vim000064400000000000000000000060121046102023000151350ustar 00000000000000" ALE configuration for debian-lsp " Source this file in your .vimrc or init.vim: " source /path/to/debian-lsp/ale-debian-lsp.vim " " You can customize the executable path by setting g:debian_lsp_executable " before sourcing this file: " let g:debian_lsp_executable = '/custom/path/to/debian-lsp' " source /path/to/debian-lsp/ale-debian-lsp.vim " Set default executable path if not already configured if !exists('g:debian_lsp_executable') let g:debian_lsp_executable = expand(':p:h') . '/target/release/debian-lsp' endif " Register debian-lsp with ALE for all supported file types let g:ale_linters = get(g:, 'ale_linters', {}) let g:ale_linters.debcontrol = ['debian-lsp'] let g:ale_linters.debcopyright = ['debian-lsp'] let g:ale_linters.debchangelog = ['debian-lsp'] let g:ale_linters.debsources = ['debian-lsp'] let g:ale_linters.make = get(g:ale_linters, 'make', []) + ['debian-lsp'] let g:ale_linters.yaml = get(g:ale_linters, 'yaml', []) + ['debian-lsp'] " Define debian-lsp for debian/control files call ale#linter#Define('debcontrol', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Define debian-lsp for debian/copyright files call ale#linter#Define('debcopyright', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Define debian-lsp for debian/changelog files call ale#linter#Define('debchangelog', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Define debian-lsp for debian/source/format files call ale#linter#Define('debsources', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Define debian-lsp for debian/watch and debian/tests/control files (make filetype) call ale#linter#Define('make', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Define debian-lsp for debian/upstream/metadata files (yaml filetype) call ale#linter#Define('yaml', { \ 'name': 'debian-lsp', \ 'lsp': 'stdio', \ 'executable': g:debian_lsp_executable, \ 'command': '%e', \ 'project_root': function('ale#handlers#lsp#GetProjectRoot'), \}) " Set filetypes for Debian packaging files augroup debian_filetypes autocmd! autocmd BufNewFile,BufRead */debian/control setfiletype debcontrol autocmd BufNewFile,BufRead */debian/copyright setfiletype debcopyright autocmd BufNewFile,BufRead */debian/changelog setfiletype debchangelog autocmd BufNewFile,BufRead */debian/source/format setfiletype debsources " watch and tests/control are typically detected as 'make' filetype augroup END debian-lsp-0.1.4/coc-debian/.npmignore000064400000000000000000000000471046102023000156300ustar 00000000000000src/ tsconfig.json jest.config.js *.ts debian-lsp-0.1.4/coc-debian/README.md000064400000000000000000000041351046102023000151120ustar 00000000000000# coc-debian A coc.nvim extension that provides Language Server Protocol support for Debian control files. ## Features - Auto-completion for Debian control file field names - Package name suggestions - Automatic activation for `debian/control` files ## Installation ### Prerequisites - [coc.nvim](https://github.com/neoclide/coc.nvim) installed in Vim/Neovim - Node.js and npm - The debian-lsp server built and available ### Local Installation 1. **Build the debian-lsp server first:** ```bash cd /path/to/debian-lsp cargo build --release ``` 2. **Install and build the coc extension:** ```bash cd coc-debian npm install npm run build ``` 3. **Install the extension in coc.nvim:** ```vim :CocInstall file:///absolute/path/to/debian-lsp/coc-debian ``` Or alternatively, create a symlink in your coc extensions directory: ```bash ln -s /absolute/path/to/debian-lsp/coc-debian ~/.config/coc/extensions/node_modules/coc-debian ``` 4. **Configure the LSP server path:** Add the following to your coc-settings.json (`:CocConfig` in Vim): ```json { "debian.enable": true, "debian.serverPath": "/absolute/path/to/debian-lsp/target/release/debian-lsp" } ``` ### Verify Installation 1. Open a Debian control file (`debian/control` or any file named `control`) 2. Try typing field names and you should see completions 3. Check `:CocList extensions` to see if `coc-debian` is listed and active ## Configuration Available settings in coc-settings.json: - `debian.enable` (boolean, default: true) - Enable/disable the extension - `debian.serverPath` (string, default: "debian-lsp") - Path to the debian-lsp executable ## Development To work on the extension: ```bash # Watch for changes and rebuild npm run watch # After making changes, restart coc :CocRestart ``` ## Troubleshooting - **LSP not starting:** Check that the `debian.serverPath` points to the correct executable - **No completions:** Verify the file is named `control` or is in a `debian/` directory - **Extension not loading:** Check `:CocList extensions` and look for any error messagesdebian-lsp-0.1.4/coc-debian/jest.config.js000064400000000000000000000003661046102023000164040ustar 00000000000000module.exports = { preset: 'ts-jest', testEnvironment: 'node', roots: ['/src'], testMatch: ['**/__tests__/**/*.ts', '**/*.test.ts'], transform: { '^.+\\.ts$': 'ts-jest', }, moduleFileExtensions: ['ts', 'js', 'json'], };debian-lsp-0.1.4/coc-debian/package-lock.json000064400000000000000000004004431046102023000170510ustar 00000000000000{ "name": "coc-debian", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "coc-debian", "version": "0.1.0", "license": "MIT", "devDependencies": { "@types/jest": "^29.0.0", "@types/node": "^12.12.0", "coc.nvim": "^0.0.82", "jest": "^29.0.0", "ts-jest": "^29.0.0", "typescript": "^4.9.0" }, "engines": { "coc": "^0.0.80" } }, "node_modules/@ampproject/remapping": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/@ampproject/remapping/-/remapping-2.3.0.tgz", "integrity": "sha512-30iZtAPgz+LTIYoeivqYo853f02jBYSd5uGnGpkFV0M3xOt9aN73erkgYAmZU43x4VfqcnLxW9Kpg3R5LC4YYw==", "dev": true, "dependencies": { "@jridgewell/gen-mapping": "^0.3.5", "@jridgewell/trace-mapping": "^0.3.24" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/code-frame": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.27.1.tgz", "integrity": "sha512-cjQ7ZlQ0Mv3b47hABuTevyTuYN4i+loJKGeV9flcCgIK37cCXRh+L1bd3iBHlynerhQ7BhCkn2BPbQUL+rGqFg==", "dev": true, "dependencies": { "@babel/helper-validator-identifier": "^7.27.1", "js-tokens": "^4.0.0", "picocolors": "^1.1.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/compat-data": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/compat-data/-/compat-data-7.28.0.tgz", "integrity": "sha512-60X7qkglvrap8mn1lh2ebxXdZYtUcpd7gsmy9kLaBJ4i/WdY8PqTSdxyA8qraikqKQK5C1KRBKXqznrVapyNaw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/core": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/core/-/core-7.28.0.tgz", "integrity": "sha512-UlLAnTPrFdNGoFtbSXwcGFQBtQZJCNjaN6hQNP3UPvuNXT1i82N26KL3dZeIpNalWywr9IuQuncaAfUaS1g6sQ==", "dev": true, "dependencies": { "@ampproject/remapping": "^2.2.0", "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-compilation-targets": "^7.27.2", "@babel/helper-module-transforms": "^7.27.3", "@babel/helpers": "^7.27.6", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/traverse": "^7.28.0", "@babel/types": "^7.28.0", "convert-source-map": "^2.0.0", "debug": "^4.1.0", "gensync": "^1.0.0-beta.2", "json5": "^2.2.3", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/babel" } }, "node_modules/@babel/generator": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.28.0.tgz", "integrity": "sha512-lJjzvrbEeWrhB4P3QBsH7tey117PjLZnDbLiQEKjQ/fNJTjuq4HSqgFA+UNSwZT8D7dxxbnuSBMsa1lrWzKlQg==", "dev": true, "dependencies": { "@babel/parser": "^7.28.0", "@babel/types": "^7.28.0", "@jridgewell/gen-mapping": "^0.3.12", "@jridgewell/trace-mapping": "^0.3.28", "jsesc": "^3.0.2" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-compilation-targets": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/helper-compilation-targets/-/helper-compilation-targets-7.27.2.tgz", "integrity": "sha512-2+1thGUUWWjLTYTHZWK1n8Yga0ijBz1XAhUXcKy81rd5g6yh7hGqMp45v7cadSbEHc9G3OTv45SyneRN3ps4DQ==", "dev": true, "dependencies": { "@babel/compat-data": "^7.27.2", "@babel/helper-validator-option": "^7.27.1", "browserslist": "^4.24.0", "lru-cache": "^5.1.1", "semver": "^6.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-globals": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/helper-globals/-/helper-globals-7.28.0.tgz", "integrity": "sha512-+W6cISkXFa1jXsDEdYA8HeevQT/FULhxzR99pxphltZcVaugps53THCeiWA8SguxxpSp3gKPiuYfSWopkLQ4hw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-imports": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-module-imports/-/helper-module-imports-7.27.1.tgz", "integrity": "sha512-0gSFWUPNXNopqtIPQvlD5WgXYI5GY2kP2cCvoT8kczjbfcfuIljTbcWrulD1CIPIX2gt1wghbDy08yE1p+/r3w==", "dev": true, "dependencies": { "@babel/traverse": "^7.27.1", "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-module-transforms": { "version": "7.27.3", "resolved": "https://registry.npmjs.org/@babel/helper-module-transforms/-/helper-module-transforms-7.27.3.tgz", "integrity": "sha512-dSOvYwvyLsWBeIRyOeHXp5vPj5l1I011r52FM1+r1jCERv+aFXYk4whgQccYEGYxK2H3ZAIA8nuPkQ0HaUo3qg==", "dev": true, "dependencies": { "@babel/helper-module-imports": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1", "@babel/traverse": "^7.27.3" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/@babel/helper-plugin-utils": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-plugin-utils/-/helper-plugin-utils-7.27.1.tgz", "integrity": "sha512-1gn1Up5YXka3YYAHGKpbideQ5Yjf1tDa9qYcgysz+cNCXukyLl6DjPXhD3VRwSb8c0J9tA4b2+rHEZtc6R0tlw==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-string-parser": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-string-parser/-/helper-string-parser-7.27.1.tgz", "integrity": "sha512-qMlSxKbpRlAridDExk92nSobyDdpPijUq2DW6oDnUqd0iOGxmQjyqhMIihI9+zv4LPyZdRje2cavWPbCbWm3eA==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-identifier": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.27.1.tgz", "integrity": "sha512-D2hP9eA+Sqx1kBZgzxZh0y1trbuU+JoDkiEwqhQ36nodYqJwyEIhPSdMNd7lOm/4io72luTPWH20Yda0xOuUow==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helper-validator-option": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/helper-validator-option/-/helper-validator-option-7.27.1.tgz", "integrity": "sha512-YvjJow9FxbhFFKDSuFnVCe2WxXk1zWc22fFePVNEaWJEu8IrZVlda6N0uHwzZrUM1il7NC9Mlp4MaJYbYd9JSg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/helpers": { "version": "7.27.6", "resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.27.6.tgz", "integrity": "sha512-muE8Tt8M22638HU31A3CgfSUciwz1fhATfoVai05aPXGor//CdWDCbnlY1yvBPo07njuVOCNGCSp/GTt12lIug==", "dev": true, "dependencies": { "@babel/template": "^7.27.2", "@babel/types": "^7.27.6" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/parser": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.28.0.tgz", "integrity": "sha512-jVZGvOxOuNSsuQuLRTh13nU0AogFlw32w/MT+LV6D3sP5WdbW61E77RnkbaO2dUvmPAYrBDJXGn5gGS6tH4j8g==", "dev": true, "dependencies": { "@babel/types": "^7.28.0" }, "bin": { "parser": "bin/babel-parser.js" }, "engines": { "node": ">=6.0.0" } }, "node_modules/@babel/plugin-syntax-async-generators": { "version": "7.8.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-async-generators/-/plugin-syntax-async-generators-7.8.4.tgz", "integrity": "sha512-tycmZxkGfZaxhMRbXlPXuVFpdWlXpir2W4AMhSJgRKzk/eDlIXOhb2LHWoLpDF7TEHylV5zNhykX6KAgHJmTNw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-bigint": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-bigint/-/plugin-syntax-bigint-7.8.3.tgz", "integrity": "sha512-wnTnFlG+YxQm3vDxpGE57Pj0srRU4sHE/mDkt1qv2YJJSeUAec2ma4WLUnUPeKjyrfntVwe/N6dCXpU+zL3Npg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-class-properties": { "version": "7.12.13", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-properties/-/plugin-syntax-class-properties-7.12.13.tgz", "integrity": "sha512-fm4idjKla0YahUNgFNLCB0qySdsoPiZP3iQE3rky0mBUtMZ23yDJ9SJdg6dXTSDnulOVqiF3Hgr9nbXvXTQZYA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.12.13" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-class-static-block": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-class-static-block/-/plugin-syntax-class-static-block-7.14.5.tgz", "integrity": "sha512-b+YyPmr6ldyNnM6sqYeMWE+bgJcJpO6yS4QD7ymxgH34GBPNDM/THBh8iunyvKIZztiwLH4CJZ0RxTk9emgpjw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-import-attributes": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-attributes/-/plugin-syntax-import-attributes-7.27.1.tgz", "integrity": "sha512-oFT0FrKHgF53f4vOsZGi2Hh3I35PfSmVs4IBFLFj4dnafP+hIWDLg3VyKmUHfLoLHlyxY4C7DGtmHuJgn+IGww==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-import-meta": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-import-meta/-/plugin-syntax-import-meta-7.10.4.tgz", "integrity": "sha512-Yqfm+XDx0+Prh3VSeEQCPU81yC+JWZ2pDPFSS4ZdpfZhp4MkFMaDC1UqseovEKwSUpnIL7+vK+Clp7bfh0iD7g==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-json-strings": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-json-strings/-/plugin-syntax-json-strings-7.8.3.tgz", "integrity": "sha512-lY6kdGpWHvjoe2vk4WrAapEuBR69EMxZl+RoGRhrFGNYVK8mOPAW8VfbT/ZgrFbXlDNiiaxQnAtgVCZ6jv30EA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-jsx": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-jsx/-/plugin-syntax-jsx-7.27.1.tgz", "integrity": "sha512-y8YTNIeKoyhGd9O0Jiyzyyqk8gdjnumGTQPsz0xOZOQ2RmkVJeZ1vmmfIvFEKqucBG6axJGBZDE/7iI5suUI/w==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-logical-assignment-operators": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-logical-assignment-operators/-/plugin-syntax-logical-assignment-operators-7.10.4.tgz", "integrity": "sha512-d8waShlpFDinQ5MtvGU9xDAOzKH47+FFoney2baFIoMr952hKOLp1HR7VszoZvOsV/4+RRszNY7D17ba0te0ig==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-nullish-coalescing-operator": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-nullish-coalescing-operator/-/plugin-syntax-nullish-coalescing-operator-7.8.3.tgz", "integrity": "sha512-aSff4zPII1u2QD7y+F8oDsz19ew4IGEJg9SVW+bqwpwtfFleiQDMdzA/R+UlWDzfnHFCxxleFT0PMIrR36XLNQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-numeric-separator": { "version": "7.10.4", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-numeric-separator/-/plugin-syntax-numeric-separator-7.10.4.tgz", "integrity": "sha512-9H6YdfkcK/uOnY/K7/aA2xpzaAgkQn37yzWUMRK7OaPOqOpGS1+n0H5hxT9AUw9EsSjPW8SVyMJwYRtWs3X3ug==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.10.4" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-object-rest-spread": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-object-rest-spread/-/plugin-syntax-object-rest-spread-7.8.3.tgz", "integrity": "sha512-XoqMijGZb9y3y2XskN+P1wUGiVwWZ5JmoDRwx5+3GmEplNyVM2s2Dg8ILFQm8rWM48orGy5YpI5Bl8U1y7ydlA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-catch-binding": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-catch-binding/-/plugin-syntax-optional-catch-binding-7.8.3.tgz", "integrity": "sha512-6VPD0Pc1lpTqw0aKoeRTMiB+kWhAoT24PA+ksWSBrFtl5SIRVpZlwN3NNPQjehA2E/91FV3RjLWoVTglWcSV3Q==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-optional-chaining": { "version": "7.8.3", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-optional-chaining/-/plugin-syntax-optional-chaining-7.8.3.tgz", "integrity": "sha512-KoK9ErH1MBlCPxV0VANkXW2/dw4vlbGDrFgz8bmUsBGYkFRcbRwMh6cIJubdPrkxRwuGdtCk0v/wPTKbQgBjkg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.8.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-private-property-in-object": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-private-property-in-object/-/plugin-syntax-private-property-in-object-7.14.5.tgz", "integrity": "sha512-0wVnp9dxJ72ZUJDV27ZfbSj6iHLoytYZmh3rFcxNnvsJF3ktkzLDZPy/mA17HGsaQT3/DQsWYX1f1QGWkCoVUg==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-top-level-await": { "version": "7.14.5", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-top-level-await/-/plugin-syntax-top-level-await-7.14.5.tgz", "integrity": "sha512-hx++upLv5U1rgYfwe1xBQUhRmU41NEvpUvrp8jkrSCdvGSnM5/qdRMtylJ6PG5OFkBaHkbTAKTnd3/YyESRHFw==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.14.5" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/plugin-syntax-typescript": { "version": "7.27.1", "resolved": "https://registry.npmjs.org/@babel/plugin-syntax-typescript/-/plugin-syntax-typescript-7.27.1.tgz", "integrity": "sha512-xfYCBMxveHrRMnAWl1ZlPXOZjzkN82THFvLhQhFXFt81Z5HnN+EtUkZhv/zcKpmT3fzmWZB0ywiBrbC3vogbwQ==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.27.1" }, "engines": { "node": ">=6.9.0" }, "peerDependencies": { "@babel/core": "^7.0.0-0" } }, "node_modules/@babel/template": { "version": "7.27.2", "resolved": "https://registry.npmjs.org/@babel/template/-/template-7.27.2.tgz", "integrity": "sha512-LPDZ85aEJyYSd18/DkjNh4/y1ntkE5KwUHWTiqgRxruuZL2F1yuHligVHLvcHY2vMHXttKFpJn6LwfI7cw7ODw==", "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/parser": "^7.27.2", "@babel/types": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/traverse": { "version": "7.28.0", "resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.28.0.tgz", "integrity": "sha512-mGe7UK5wWyh0bKRfupsUchrQGqvDbZDbKJw+kcRGSmdHVYrv+ltd0pnpDTVpiTqnaBru9iEvA8pz8W46v0Amwg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.27.1", "@babel/generator": "^7.28.0", "@babel/helper-globals": "^7.28.0", "@babel/parser": "^7.28.0", "@babel/template": "^7.27.2", "@babel/types": "^7.28.0", "debug": "^4.3.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@babel/types": { "version": "7.28.1", "resolved": "https://registry.npmjs.org/@babel/types/-/types-7.28.1.tgz", "integrity": "sha512-x0LvFTekgSX+83TI28Y9wYPUfzrnl2aT5+5QLnO6v7mSJYtEEevuDRN0F0uSHRk1G1IWZC43o00Y0xDDrpBGPQ==", "dev": true, "dependencies": { "@babel/helper-string-parser": "^7.27.1", "@babel/helper-validator-identifier": "^7.27.1" }, "engines": { "node": ">=6.9.0" } }, "node_modules/@bcoe/v8-coverage": { "version": "0.2.3", "resolved": "https://registry.npmjs.org/@bcoe/v8-coverage/-/v8-coverage-0.2.3.tgz", "integrity": "sha512-0hYQ8SB4Db5zvZB4axdMHGwEaQjkZzFjQiN9LVYvIFB2nSUHW9tYpxWriPrWDASIxiaXax83REcLxuSdnGPZtw==", "dev": true }, "node_modules/@istanbuljs/load-nyc-config": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/@istanbuljs/load-nyc-config/-/load-nyc-config-1.1.0.tgz", "integrity": "sha512-VjeHSlIzpv/NyD3N0YuHfXOPDIixcA1q2ZV98wsMqcYlPmv2n3Yb2lYP9XMElnaFVXg5A7YLTeLu6V84uQDjmQ==", "dev": true, "dependencies": { "camelcase": "^5.3.1", "find-up": "^4.1.0", "get-package-type": "^0.1.0", "js-yaml": "^3.13.1", "resolve-from": "^5.0.0" }, "engines": { "node": ">=8" } }, "node_modules/@istanbuljs/schema": { "version": "0.1.3", "resolved": "https://registry.npmjs.org/@istanbuljs/schema/-/schema-0.1.3.tgz", "integrity": "sha512-ZXRY4jNvVgSVQ8DL3LTcakaAtXwTVUxE81hslsyD2AtoXW/wVob10HkOJ1X/pAlcI7D+2YoZKg5do8G/w6RYgA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/@jest/console": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/console/-/console-29.7.0.tgz", "integrity": "sha512-5Ni4CU7XHQi32IJ398EEP4RrB8eV09sXP2ROqD4bksHrnTree52PsxvX8tpL8LvTZ3pFzXyPbNQReSN41CAhOg==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/core": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/core/-/core-29.7.0.tgz", "integrity": "sha512-n7aeXWKMnGtDA48y8TLWJPJmLmmZ642Ceo78cYWEpiD7FzDgmNDV/GCVRorPABdXLJZ/9wzzgZAlHjXjxDHGsg==", "dev": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/reporters": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "ci-info": "^3.2.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-changed-files": "^29.7.0", "jest-config": "^29.7.0", "jest-haste-map": "^29.7.0", "jest-message-util": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-resolve-dependencies": "^29.7.0", "jest-runner": "^29.7.0", "jest-runtime": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "jest-watcher": "^29.7.0", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-ansi": "^6.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { "node-notifier": { "optional": true } } }, "node_modules/@jest/environment": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/environment/-/environment-29.7.0.tgz", "integrity": "sha512-aQIfHDq33ExsN4jP1NWGXhxgQ/wixs60gDiKO+XVMd8Mn0NWPWgc34ZQDTb2jKaUWQ7MuwoitXAsN2XVXNMpAw==", "dev": true, "dependencies": { "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect/-/expect-29.7.0.tgz", "integrity": "sha512-8uMeAMycttpva3P1lBHB8VciS9V0XAr3GymPpipdyQXbBcuhkLQOSe8E/p92RyAdToS6ZD1tFkX+CkhoECE0dQ==", "dev": true, "dependencies": { "expect": "^29.7.0", "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/expect-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/expect-utils/-/expect-utils-29.7.0.tgz", "integrity": "sha512-GlsNBWiFQFCVi9QVSx7f5AgMeLxe9YCCs5PuP2O2LdjDAA8Jh9eX7lA1Jq/xdXw3Wb3hyvlFNfZIfcRetSzYcA==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/fake-timers": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/fake-timers/-/fake-timers-29.7.0.tgz", "integrity": "sha512-q4DH1Ha4TTFPdxLsqDXK1d3+ioSL7yL5oCMJZgDYm6i+6CygW5E5xVr/D1HdsGxjt1ZWSfUAs9OxSB/BNelWrQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@sinonjs/fake-timers": "^10.0.2", "@types/node": "*", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/globals": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/globals/-/globals-29.7.0.tgz", "integrity": "sha512-mpiz3dutLbkW2MNFubUGUEVLkTGiqW6yLVTA+JbP6fI6J5iL9Y0Nlg8k95pcF8ctKwCS7WVxteBs29hhfAotzQ==", "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/types": "^29.6.3", "jest-mock": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/reporters": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/reporters/-/reporters-29.7.0.tgz", "integrity": "sha512-DApq0KJbJOEzAFYjHADNNxAE3KbhxQB1y5Kplb5Waqw6zVbuWatSnMjE5gs8FUgEPmNsnZA3NCWl9NG0ia04Pg==", "dev": true, "dependencies": { "@bcoe/v8-coverage": "^0.2.3", "@jest/console": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "@types/node": "*", "chalk": "^4.0.0", "collect-v8-coverage": "^1.0.0", "exit": "^0.1.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "istanbul-lib-coverage": "^3.0.0", "istanbul-lib-instrument": "^6.0.0", "istanbul-lib-report": "^3.0.0", "istanbul-lib-source-maps": "^4.0.0", "istanbul-reports": "^3.1.3", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "slash": "^3.0.0", "string-length": "^4.0.1", "strip-ansi": "^6.0.0", "v8-to-istanbul": "^9.0.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { "node-notifier": { "optional": true } } }, "node_modules/@jest/schemas": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/schemas/-/schemas-29.6.3.tgz", "integrity": "sha512-mo5j5X+jIZmJQveBKeS/clAueipV7KgiX1vMgCxam1RNYiqE1w62n0/tJJnHtjW8ZHcQco5gY85jA3mi0L+nSA==", "dev": true, "dependencies": { "@sinclair/typebox": "^0.27.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/source-map": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/source-map/-/source-map-29.6.3.tgz", "integrity": "sha512-MHjT95QuipcPrpLM+8JMSzFx6eHp5Bm+4XeFDJlwsvVBjmKNiIAvasGK2fxz2WbGRlnvqehFbh07MMa7n3YJnw==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.18", "callsites": "^3.0.0", "graceful-fs": "^4.2.9" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/test-result": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-result/-/test-result-29.7.0.tgz", "integrity": "sha512-Fdx+tv6x1zlkJPcWXmMDAG2HBnaR9XPSd5aDWQVsfrZmLVT3lU1cwyxLgRmXR9yrq4NBoEm9BMsfgFzTQAbJYA==", "dev": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/types": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "collect-v8-coverage": "^1.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/test-sequencer": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/test-sequencer/-/test-sequencer-29.7.0.tgz", "integrity": "sha512-GQwJ5WZVrKnOJuiYiAF52UNUJXgTZx1NHjFSEB0qEMmSZKAkdMoIzw/Cj6x6NF4AvV23AUqDpFzQkN/eYCYTxw==", "dev": true, "dependencies": { "@jest/test-result": "^29.7.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/transform": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/@jest/transform/-/transform-29.7.0.tgz", "integrity": "sha512-ok/BTPFzFKVMwO5eOHRrvnBVHdRy9IrsrW1GpMaQ9MCnilNLXQKmAX8s1YXDFaai9xJpac2ySzV0YeRRECr2Vw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/types": "^29.6.3", "@jridgewell/trace-mapping": "^0.3.18", "babel-plugin-istanbul": "^6.1.1", "chalk": "^4.0.0", "convert-source-map": "^2.0.0", "fast-json-stable-stringify": "^2.1.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "micromatch": "^4.0.4", "pirates": "^4.0.4", "slash": "^3.0.0", "write-file-atomic": "^4.0.2" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jest/types": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/@jest/types/-/types-29.6.3.tgz", "integrity": "sha512-u3UPsIilWKOM3F9CXtrG8LEJmNxwoCQC/XVj4IKYXvvpx7QIi/Kg1LI5uDmDpKlac62NUtX7eLjRh+jVZcLOzw==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "@types/istanbul-lib-coverage": "^2.0.0", "@types/istanbul-reports": "^3.0.0", "@types/node": "*", "@types/yargs": "^17.0.8", "chalk": "^4.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/@jridgewell/gen-mapping": { "version": "0.3.12", "resolved": "https://registry.npmjs.org/@jridgewell/gen-mapping/-/gen-mapping-0.3.12.tgz", "integrity": "sha512-OuLGC46TjB5BbN1dH8JULVVZY4WTdkF7tV9Ys6wLL1rubZnCMstOhNHueU5bLCrnRuDhKPDM4g6sw4Bel5Gzqg==", "dev": true, "dependencies": { "@jridgewell/sourcemap-codec": "^1.5.0", "@jridgewell/trace-mapping": "^0.3.24" } }, "node_modules/@jridgewell/resolve-uri": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/@jridgewell/resolve-uri/-/resolve-uri-3.1.2.tgz", "integrity": "sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==", "dev": true, "engines": { "node": ">=6.0.0" } }, "node_modules/@jridgewell/sourcemap-codec": { "version": "1.5.4", "resolved": "https://registry.npmjs.org/@jridgewell/sourcemap-codec/-/sourcemap-codec-1.5.4.tgz", "integrity": "sha512-VT2+G1VQs/9oz078bLrYbecdZKs912zQlkelYpuf+SXF+QvZDYJlbx/LSx+meSAwdDFnF8FVXW92AVjjkVmgFw==", "dev": true }, "node_modules/@jridgewell/trace-mapping": { "version": "0.3.29", "resolved": "https://registry.npmjs.org/@jridgewell/trace-mapping/-/trace-mapping-0.3.29.tgz", "integrity": "sha512-uw6guiW/gcAGPDhLmd77/6lW8QLeiV5RUTsAX46Db6oLhGaVj4lhnPwb184s1bkc8kdVg/+h988dro8GRDpmYQ==", "dev": true, "dependencies": { "@jridgewell/resolve-uri": "^3.1.0", "@jridgewell/sourcemap-codec": "^1.4.14" } }, "node_modules/@sinclair/typebox": { "version": "0.27.8", "resolved": "https://registry.npmjs.org/@sinclair/typebox/-/typebox-0.27.8.tgz", "integrity": "sha512-+Fj43pSMwJs4KRrH/938Uf+uAELIgVBmQzg/q1YG10djyfA3TnrU8N8XzqCh/okZdszqBQTZf96idMfE5lnwTA==", "dev": true }, "node_modules/@sinonjs/commons": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/@sinonjs/commons/-/commons-3.0.1.tgz", "integrity": "sha512-K3mCHKQ9sVh8o1C9cxkwxaOmXoAMlDxC1mYyHrjqOWEcBjYr76t96zL2zlj5dUGZ3HSw240X1qgH3Mjf1yJWpQ==", "dev": true, "dependencies": { "type-detect": "4.0.8" } }, "node_modules/@sinonjs/fake-timers": { "version": "10.3.0", "resolved": "https://registry.npmjs.org/@sinonjs/fake-timers/-/fake-timers-10.3.0.tgz", "integrity": "sha512-V4BG07kuYSUkTCSBHG8G8TNhM+F19jXFWnQtzj+we8DrkpSBCee9Z3Ms8yiGer/dlmhe35/Xdgyo3/0rQKg7YA==", "dev": true, "dependencies": { "@sinonjs/commons": "^3.0.0" } }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", "integrity": "sha512-qoQprZvz5wQFJwMDqeseRXWv3rqMvhgpbXFfVyWhbx9X47POIA6i/+dXefEmZKoAgOaTdaIgNSMqMIU61yRyzA==", "dev": true, "dependencies": { "@babel/parser": "^7.20.7", "@babel/types": "^7.20.7", "@types/babel__generator": "*", "@types/babel__template": "*", "@types/babel__traverse": "*" } }, "node_modules/@types/babel__generator": { "version": "7.27.0", "resolved": "https://registry.npmjs.org/@types/babel__generator/-/babel__generator-7.27.0.tgz", "integrity": "sha512-ufFd2Xi92OAVPYsy+P4n7/U7e68fex0+Ee8gSG9KX7eo084CWiQ4sdxktvdl0bOPupXtVJPY19zk6EwWqUQ8lg==", "dev": true, "dependencies": { "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__template": { "version": "7.4.4", "resolved": "https://registry.npmjs.org/@types/babel__template/-/babel__template-7.4.4.tgz", "integrity": "sha512-h/NUaSyG5EyxBIp8YRxo4RMe2/qQgvyowRwVMzhYhBCONbW8PUsg4lkFMrhgZhUe5z3L3MiLDuvyJ/CaPa2A8A==", "dev": true, "dependencies": { "@babel/parser": "^7.1.0", "@babel/types": "^7.0.0" } }, "node_modules/@types/babel__traverse": { "version": "7.20.7", "resolved": "https://registry.npmjs.org/@types/babel__traverse/-/babel__traverse-7.20.7.tgz", "integrity": "sha512-dkO5fhS7+/oos4ciWxyEyjWe48zmG6wbCheo/G2ZnHx4fs3EU6YC6UM8rk56gAjNJ9P3MTH2jo5jb92/K6wbng==", "dev": true, "dependencies": { "@babel/types": "^7.20.7" } }, "node_modules/@types/graceful-fs": { "version": "4.1.9", "resolved": "https://registry.npmjs.org/@types/graceful-fs/-/graceful-fs-4.1.9.tgz", "integrity": "sha512-olP3sd1qOEe5dXTSaFvQG+02VdRXcdytWLAZsAq1PecU8uqQAhkrnbli7DagjtXKW/Bl7YJbUsa8MPcuc8LHEQ==", "dev": true, "dependencies": { "@types/node": "*" } }, "node_modules/@types/istanbul-lib-coverage": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-coverage/-/istanbul-lib-coverage-2.0.6.tgz", "integrity": "sha512-2QF/t/auWm0lsy8XtKVPG19v3sSOQlJe/YHZgfjb/KBBHOGSV+J2q/S671rcq9uTBrLAXmZpqJiaQbMT+zNU1w==", "dev": true }, "node_modules/@types/istanbul-lib-report": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/@types/istanbul-lib-report/-/istanbul-lib-report-3.0.3.tgz", "integrity": "sha512-NQn7AHQnk/RSLOxrBbGyJM/aVQ+pjj5HCgasFxc0K/KhoATfQ/47AyUl15I2yBUpihjmas+a+VJBOqecrFH+uA==", "dev": true, "dependencies": { "@types/istanbul-lib-coverage": "*" } }, "node_modules/@types/istanbul-reports": { "version": "3.0.4", "resolved": "https://registry.npmjs.org/@types/istanbul-reports/-/istanbul-reports-3.0.4.tgz", "integrity": "sha512-pk2B1NWalF9toCRu6gjBzR69syFjP4Od8WRAX+0mmf9lAjCRicLOWc+ZrxZHx/0XRjotgkF9t6iaMJ+aXcOdZQ==", "dev": true, "dependencies": { "@types/istanbul-lib-report": "*" } }, "node_modules/@types/jest": { "version": "29.5.14", "resolved": "https://registry.npmjs.org/@types/jest/-/jest-29.5.14.tgz", "integrity": "sha512-ZN+4sdnLUbo8EVvVc2ao0GFW6oVrQRPn4K2lglySj7APvSrgzxHiNNK99us4WDMi57xxA2yggblIAMNhXOotLQ==", "dev": true, "dependencies": { "expect": "^29.0.0", "pretty-format": "^29.0.0" } }, "node_modules/@types/node": { "version": "12.20.55", "resolved": "https://registry.npmjs.org/@types/node/-/node-12.20.55.tgz", "integrity": "sha512-J8xLz7q2OFulZ2cyGTLE1TbbZcjpno7FaN6zdJNrgAdrJ+DZzh/uFR6YrTb4C+nXakvud8Q4+rbhoIWlYQbUFQ==", "dev": true }, "node_modules/@types/stack-utils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@types/stack-utils/-/stack-utils-2.0.3.tgz", "integrity": "sha512-9aEbYZ3TbYMznPdcdr3SmIrLXwC/AKZXQeCf9Pgao5CKb8CyHuEX5jzWPTkvregvhRJHcpRO6BFoGW9ycaOkYw==", "dev": true }, "node_modules/@types/yargs": { "version": "17.0.33", "resolved": "https://registry.npmjs.org/@types/yargs/-/yargs-17.0.33.tgz", "integrity": "sha512-WpxBCKWPLr4xSsHgz511rFJAM+wS28w2zEO1QDNY5zM/S8ok70NNfztH0xwhqKyaK0OHCbN98LDAZuy1ctxDkA==", "dev": true, "dependencies": { "@types/yargs-parser": "*" } }, "node_modules/@types/yargs-parser": { "version": "21.0.3", "resolved": "https://registry.npmjs.org/@types/yargs-parser/-/yargs-parser-21.0.3.tgz", "integrity": "sha512-I4q9QU9MQv4oEOz4tAHJtNz1cwuLxn2F3xcc2iV5WdqLPpUnj30aUuxt1mAxYTG+oe8CZMV/+6rU4S4gRDzqtQ==", "dev": true }, "node_modules/ansi-escapes": { "version": "4.3.2", "resolved": "https://registry.npmjs.org/ansi-escapes/-/ansi-escapes-4.3.2.tgz", "integrity": "sha512-gKXj5ALrKWQLsYG9jlTRmR/xKluxHV+Z9QEwNIgCfM1/uwPMCuzVVnh5mwTd+OuBZcwSIMbqssNWRm1lE51QaQ==", "dev": true, "dependencies": { "type-fest": "^0.21.3" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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/ansi-styles": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", "dev": true, "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/anymatch": { "version": "3.1.3", "resolved": "https://registry.npmjs.org/anymatch/-/anymatch-3.1.3.tgz", "integrity": "sha512-KMReFUr0B4t+D+OBkjR3KYqvocp2XaSzO55UcB6mgQMd3KbcE+mWTyvVV7D/zsdEbNnV6acZUutkiHQXvTr1Rw==", "dev": true, "dependencies": { "normalize-path": "^3.0.0", "picomatch": "^2.0.4" }, "engines": { "node": ">= 8" } }, "node_modules/argparse": { "version": "1.0.10", "resolved": "https://registry.npmjs.org/argparse/-/argparse-1.0.10.tgz", "integrity": "sha512-o5Roy6tNG4SL/FOkCAN6RzjiakZS25RLYFrcMttJqbdd8BWrnA+fGz57iN5Pb06pvBGvl5gQ0B48dJlslXvoTg==", "dev": true, "dependencies": { "sprintf-js": "~1.0.2" } }, "node_modules/async": { "version": "3.2.6", "resolved": "https://registry.npmjs.org/async/-/async-3.2.6.tgz", "integrity": "sha512-htCUDlxyyCLMgaM3xXg0C0LW2xqfuQ6p05pCEIsXuyQ+a1koYKTuBMzRNwmybfLgvJDMd0r1LTn4+E0Ti6C2AA==", "dev": true }, "node_modules/babel-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/babel-jest/-/babel-jest-29.7.0.tgz", "integrity": "sha512-BrvGY3xZSwEcCzKvKsCi2GgHqDqsYkOP4/by5xCgIwGXQxIEh+8ew3gmrE1y7XRR6LHZIj6yLYnUi/mm2KXKBg==", "dev": true, "dependencies": { "@jest/transform": "^29.7.0", "@types/babel__core": "^7.1.14", "babel-plugin-istanbul": "^6.1.1", "babel-preset-jest": "^29.6.3", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@babel/core": "^7.8.0" } }, "node_modules/babel-plugin-istanbul": { "version": "6.1.1", "resolved": "https://registry.npmjs.org/babel-plugin-istanbul/-/babel-plugin-istanbul-6.1.1.tgz", "integrity": "sha512-Y1IQok9821cC9onCx5otgFfRm7Lm+I+wwxOx738M/WLPZ9Q42m4IG5W0FNX8WLL2gYMZo3JkuXIH2DOpWM+qwA==", "dev": true, "dependencies": { "@babel/helper-plugin-utils": "^7.0.0", "@istanbuljs/load-nyc-config": "^1.0.0", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-instrument": "^5.0.4", "test-exclude": "^6.0.0" }, "engines": { "node": ">=8" } }, "node_modules/babel-plugin-istanbul/node_modules/istanbul-lib-instrument": { "version": "5.2.1", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-5.2.1.tgz", "integrity": "sha512-pzqtp31nLv/XFOzXGuvhCb8qhjmTVo5vjVk19XE4CRlSWz0KoeJ3bw9XsA7nOp9YBf4qHjwBxkDzKcME/J29Yg==", "dev": true, "dependencies": { "@babel/core": "^7.12.3", "@babel/parser": "^7.14.7", "@istanbuljs/schema": "^0.1.2", "istanbul-lib-coverage": "^3.2.0", "semver": "^6.3.0" }, "engines": { "node": ">=8" } }, "node_modules/babel-plugin-jest-hoist": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-plugin-jest-hoist/-/babel-plugin-jest-hoist-29.6.3.tgz", "integrity": "sha512-ESAc/RJvGTFEzRwOTT4+lNDk/GNHMkKbNzsvT0qKRfDyyYTskxB5rnU2njIDYVxXCBHHEI1c0YwHob3WaYujOg==", "dev": true, "dependencies": { "@babel/template": "^7.3.3", "@babel/types": "^7.3.3", "@types/babel__core": "^7.1.14", "@types/babel__traverse": "^7.0.6" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/babel-preset-current-node-syntax": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/babel-preset-current-node-syntax/-/babel-preset-current-node-syntax-1.1.0.tgz", "integrity": "sha512-ldYss8SbBlWva1bs28q78Ju5Zq1F+8BrqBZZ0VFhLBvhh6lCpC2o3gDJi/5DRLs9FgYZCnmPYIVFU4lRXCkyUw==", "dev": true, "dependencies": { "@babel/plugin-syntax-async-generators": "^7.8.4", "@babel/plugin-syntax-bigint": "^7.8.3", "@babel/plugin-syntax-class-properties": "^7.12.13", "@babel/plugin-syntax-class-static-block": "^7.14.5", "@babel/plugin-syntax-import-attributes": "^7.24.7", "@babel/plugin-syntax-import-meta": "^7.10.4", "@babel/plugin-syntax-json-strings": "^7.8.3", "@babel/plugin-syntax-logical-assignment-operators": "^7.10.4", "@babel/plugin-syntax-nullish-coalescing-operator": "^7.8.3", "@babel/plugin-syntax-numeric-separator": "^7.10.4", "@babel/plugin-syntax-object-rest-spread": "^7.8.3", "@babel/plugin-syntax-optional-catch-binding": "^7.8.3", "@babel/plugin-syntax-optional-chaining": "^7.8.3", "@babel/plugin-syntax-private-property-in-object": "^7.14.5", "@babel/plugin-syntax-top-level-await": "^7.14.5" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/babel-preset-jest": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/babel-preset-jest/-/babel-preset-jest-29.6.3.tgz", "integrity": "sha512-0B3bhxR6snWXJZtR/RliHTDPRgn1sNHOR0yVtq/IiQFyuOVjFS+wuio/R4gSNkyYmKmJB4wGZv2NZanmKmTnNA==", "dev": true, "dependencies": { "babel-plugin-jest-hoist": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@babel/core": "^7.0.0" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "dev": true }, "node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "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/browserslist": { "version": "4.25.1", "resolved": "https://registry.npmjs.org/browserslist/-/browserslist-4.25.1.tgz", "integrity": "sha512-KGj0KoOMXLpSNkkEI6Z6mShmQy0bc1I+T7K9N81k4WWMrfz+6fQ6es80B/YLAeRoKvjYE1YSHHOW1qe9xIVzHw==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "dependencies": { "caniuse-lite": "^1.0.30001726", "electron-to-chromium": "^1.5.173", "node-releases": "^2.0.19", "update-browserslist-db": "^1.1.3" }, "bin": { "browserslist": "cli.js" }, "engines": { "node": "^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7" } }, "node_modules/bs-logger": { "version": "0.2.6", "resolved": "https://registry.npmjs.org/bs-logger/-/bs-logger-0.2.6.tgz", "integrity": "sha512-pd8DCoxmbgc7hyPKOvxtqNcjYoOsABPQdcCUjGp3d42VR2CX1ORhk2A87oqqu5R1kk+76nsxZupkmyd+MVtCog==", "dev": true, "dependencies": { "fast-json-stable-stringify": "2.x" }, "engines": { "node": ">= 6" } }, "node_modules/bser": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/bser/-/bser-2.1.1.tgz", "integrity": "sha512-gQxTNE/GAfIIrmHLUE3oJyp5FO6HRBfhjnw4/wMmA63ZGDJnWBmgY/lyQBpnDUkGmAhbSe39tx2d/iTOAfglwQ==", "dev": true, "dependencies": { "node-int64": "^0.4.0" } }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", "integrity": "sha512-E+XQCRwSbaaiChtv6k6Dwgc+bx+Bs6vuKJHHl5kox/BaKbhiXzqQOwK4cO22yElGp2OCmjwVhT3HmxgyPGnJfQ==", "dev": true }, "node_modules/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/camelcase": { "version": "5.3.1", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-5.3.1.tgz", "integrity": "sha512-L28STB170nwWS63UjtlEOE3dldQApaJXZkOI1uMFfzf3rRuPegHaHesyee+YxQ+W6SvRDQV6UrdOdRiR153wJg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/caniuse-lite": { "version": "1.0.30001727", "resolved": "https://registry.npmjs.org/caniuse-lite/-/caniuse-lite-1.0.30001727.tgz", "integrity": "sha512-pB68nIHmbN6L/4C6MH1DokyR3bYqFwjaSs/sWDHGj4CTcFtQUQMuJftVwWkXq7mNWOybD3KhUv3oWHoGxgP14Q==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/caniuse-lite" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ] }, "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/char-regex": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/char-regex/-/char-regex-1.0.2.tgz", "integrity": "sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/ci-info": { "version": "3.9.0", "resolved": "https://registry.npmjs.org/ci-info/-/ci-info-3.9.0.tgz", "integrity": "sha512-NIxF55hv4nSqQswkAeiOi1r83xy8JldOFDTWiug55KBu9Jnblncd2U6ViHmYgHf01TPZS77NJBhBMKdWj9HQMQ==", "dev": true, "funding": [ { "type": "github", "url": "https://github.com/sponsors/sibiraj-s" } ], "engines": { "node": ">=8" } }, "node_modules/cjs-module-lexer": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/cjs-module-lexer/-/cjs-module-lexer-1.4.3.tgz", "integrity": "sha512-9z8TZaGM1pfswYeXrUpzPrkx8UnWYdhJclsiYMm6x/w5+nN+8Tf/LnAgfLGQCm59qAOxU8WwHEq2vNwF6i4j+Q==", "dev": true }, "node_modules/cliui": { "version": "8.0.1", "resolved": "https://registry.npmjs.org/cliui/-/cliui-8.0.1.tgz", "integrity": "sha512-BSeNnyus75C4//NQ9gQt1/csTXyo/8Sb+afLAkzAptFuMsod9HFokGNudZpi/oQV73hnVK+sR+5PVRMd+Dr7YQ==", "dev": true, "dependencies": { "string-width": "^4.2.0", "strip-ansi": "^6.0.1", "wrap-ansi": "^7.0.0" }, "engines": { "node": ">=12" } }, "node_modules/co": { "version": "4.6.0", "resolved": "https://registry.npmjs.org/co/-/co-4.6.0.tgz", "integrity": "sha512-QVb0dM5HvG+uaxitm8wONl7jltx8dqhfU33DcqtOZcLSVIKSDDLDi7+0LbAKiyI8hD9u42m2YxXSkMGWThaecQ==", "dev": true, "engines": { "iojs": ">= 1.0.0", "node": ">= 0.12.0" } }, "node_modules/coc.nvim": { "version": "0.0.82", "resolved": "https://registry.npmjs.org/coc.nvim/-/coc.nvim-0.0.82.tgz", "integrity": "sha512-+70ap6FH8FSdaQ0CPijaasQzg6ue84j4/LkX6ZSpALX7YKBcGGDkCcd6adgaC/86b/ZqT3iTTEbMh3mdaI5qPA==", "dev": true, "engines": { "node": ">=12.12.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/cocnvim" }, "peerDependencies": { "@types/node": "^12.12.0", "typescript": "^4" } }, "node_modules/collect-v8-coverage": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/collect-v8-coverage/-/collect-v8-coverage-1.0.2.tgz", "integrity": "sha512-lHl4d5/ONEbLlJvaJNtsF/Lz+WvB07u2ycqTYbdrq7UypDXailES4valYb2eWiJFxZlVmpGekfqoxQhzyFdT4Q==", "dev": true }, "node_modules/color-convert": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", "dev": true, "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/color-name": { "version": "1.1.4", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", "dev": true }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true }, "node_modules/convert-source-map": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/convert-source-map/-/convert-source-map-2.0.0.tgz", "integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==", "dev": true }, "node_modules/create-jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/create-jest/-/create-jest-29.7.0.tgz", "integrity": "sha512-Adz2bdH0Vq3F53KEMJOoftQFutWCukm6J24wbPWRO4k1kMY7gS7ds/uoJkNuV8wDCtWWnuwGcJwpWcih+zEW1Q==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "exit": "^0.1.2", "graceful-fs": "^4.2.9", "jest-config": "^29.7.0", "jest-util": "^29.7.0", "prompts": "^2.0.1" }, "bin": { "create-jest": "bin/create-jest.js" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "node_modules/debug": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/debug/-/debug-4.4.1.tgz", "integrity": "sha512-KcKCqiftBJcZr++7ykoDIEwSa3XWowTfNPo92BYxjXiyYEVrUQh2aLyhxBCwww+heortUFxEJYcRzosstTEBYQ==", "dev": true, "dependencies": { "ms": "^2.1.3" }, "engines": { "node": ">=6.0" }, "peerDependenciesMeta": { "supports-color": { "optional": true } } }, "node_modules/dedent": { "version": "1.6.0", "resolved": "https://registry.npmjs.org/dedent/-/dedent-1.6.0.tgz", "integrity": "sha512-F1Z+5UCFpmQUzJa11agbyPVMbpgT/qA3/SKyJ1jyBgm7dUcUEa8v9JwDkerSQXfakBwFljIxhOJqGkjUwZ9FSA==", "dev": true, "peerDependencies": { "babel-plugin-macros": "^3.1.0" }, "peerDependenciesMeta": { "babel-plugin-macros": { "optional": true } } }, "node_modules/deepmerge": { "version": "4.3.1", "resolved": "https://registry.npmjs.org/deepmerge/-/deepmerge-4.3.1.tgz", "integrity": "sha512-3sUqbMEc77XqpdNO7FRyRog+eW3ph+GYCbj+rK+uYyRMuwsVy0rMiVtPn+QJlKFvWP/1PYpapqYn0Me2knFn+A==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/detect-newline": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/detect-newline/-/detect-newline-3.1.0.tgz", "integrity": "sha512-TLz+x/vEXm/Y7P7wn1EJFNLxYpUD4TgMosxY6fAVJUnJMbupHBOncxyWUG9OpTaH9EBD7uFI5LfEgmMOc54DsA==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/diff-sequences": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/diff-sequences/-/diff-sequences-29.6.3.tgz", "integrity": "sha512-EjePK1srD3P08o2j4f0ExnylqRs5B9tJjcp9t1krH2qRi8CCdsYfwe9JgSLurFBWwq4uOlipzfk5fHNvwFKr8Q==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/ejs": { "version": "3.1.10", "resolved": "https://registry.npmjs.org/ejs/-/ejs-3.1.10.tgz", "integrity": "sha512-UeJmFfOrAQS8OJWPZ4qtgHyWExa088/MtK5UEyoJGFH67cDEXkZSviOiKRCZ4Xij0zxI3JECgYs3oKx+AizQBA==", "dev": true, "dependencies": { "jake": "^10.8.5" }, "bin": { "ejs": "bin/cli.js" }, "engines": { "node": ">=0.10.0" } }, "node_modules/electron-to-chromium": { "version": "1.5.187", "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.5.187.tgz", "integrity": "sha512-cl5Jc9I0KGUoOoSbxvTywTa40uspGJt/BDBoDLoxJRSBpWh4FFXBsjNRHfQrONsV/OoEjDfHUmZQa2d6Ze4YgA==", "dev": true }, "node_modules/emittery": { "version": "0.13.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.13.1.tgz", "integrity": "sha512-DeWwawk6r5yR9jFgnDKYt4sLS0LmHJJi3ZOnb5/JdbYwj3nW+FxQnHIjhBKz8YLC7oRNPVM9NQ47I3CVx34eqQ==", "dev": true, "engines": { "node": ">=12" }, "funding": { "url": "https://github.com/sindresorhus/emittery?sponsor=1" } }, "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/error-ex": { "version": "1.3.2", "resolved": "https://registry.npmjs.org/error-ex/-/error-ex-1.3.2.tgz", "integrity": "sha512-7dFHNmqeFSEt2ZBsCriorKnn3Z2pj+fd9kmI6QoWw4//DL+icEBfc0U7qJCisqrTsKTjw4fNFy2pW9OqStD84g==", "dev": true, "dependencies": { "is-arrayish": "^0.2.1" } }, "node_modules/escalade": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/escalade/-/escalade-3.2.0.tgz", "integrity": "sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/escape-string-regexp": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-2.0.0.tgz", "integrity": "sha512-UpzcLCXolUWcNu5HtVMHYdXJjArjsF9C0aNnquZYY4uW/Vu0miy5YoWvbV345HauVvcAUnpRuhMMcqTcGOY2+w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/esprima": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/esprima/-/esprima-4.0.1.tgz", "integrity": "sha512-eGuFFw7Upda+g4p+QHvnW0RyTX/SVeJBDM/gCtMARO0cLuT2HcEKnTPvhjV6aGeqrCB/sbNop0Kszm0jsaWU4A==", "dev": true, "bin": { "esparse": "bin/esparse.js", "esvalidate": "bin/esvalidate.js" }, "engines": { "node": ">=4" } }, "node_modules/execa": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/execa/-/execa-5.1.1.tgz", "integrity": "sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==", "dev": true, "dependencies": { "cross-spawn": "^7.0.3", "get-stream": "^6.0.0", "human-signals": "^2.1.0", "is-stream": "^2.0.0", "merge-stream": "^2.0.0", "npm-run-path": "^4.0.1", "onetime": "^5.1.2", "signal-exit": "^3.0.3", "strip-final-newline": "^2.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sindresorhus/execa?sponsor=1" } }, "node_modules/exit": { "version": "0.1.2", "resolved": "https://registry.npmjs.org/exit/-/exit-0.1.2.tgz", "integrity": "sha512-Zk/eNKV2zbjpKzrsQ+n1G6poVbErQxJ0LBOJXaKZ1EViLzH+hrLu9cdXI4zw9dBQJslwBEpbQ2P1oS7nDxs6jQ==", "dev": true, "engines": { "node": ">= 0.8.0" } }, "node_modules/expect": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/expect/-/expect-29.7.0.tgz", "integrity": "sha512-2Zks0hf1VLFYI1kbh0I5jP3KHHyCHpkfyHBzsSXRFgl/Bg9mWYfMW8oD+PdMPlEwy5HNsR9JutYy6pMeOh61nw==", "dev": true, "dependencies": { "@jest/expect-utils": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true }, "node_modules/fb-watchman": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/fb-watchman/-/fb-watchman-2.0.2.tgz", "integrity": "sha512-p5161BqbuCaSnB8jIbzQHOlpgsPmK5rJVDfDKO91Axs5NC1uu3HRQm6wt9cd9/+GtQQIO53JdGXXoyDpTAsgYA==", "dev": true, "dependencies": { "bser": "2.1.1" } }, "node_modules/filelist": { "version": "1.0.4", "resolved": "https://registry.npmjs.org/filelist/-/filelist-1.0.4.tgz", "integrity": "sha512-w1cEuf3S+DrLCQL7ET6kz+gmlJdbq9J7yXCSjK/OZCPA+qEN1WyF4ZAf0YYJa4/shHJra2t/d/r8SV4Ji+x+8Q==", "dev": true, "dependencies": { "minimatch": "^5.0.1" } }, "node_modules/filelist/node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "dev": true, "dependencies": { "balanced-match": "^1.0.0" } }, "node_modules/filelist/node_modules/minimatch": { "version": "5.1.6", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.6.tgz", "integrity": "sha512-lKwV/1brpG6mBUFHtb7NUmtABCb2WZZmm2wNiOA5hAb8VdCS4B3dtMWyvcoViccwAW/COERjXLt0zP1zXUN26g==", "dev": true, "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "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/find-up": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-4.1.0.tgz", "integrity": "sha512-PpOwAdQ/YlXQ2vj8a3h8IipDuYRi3wceVQQGYWxNINccq40Anw7BlsEXCMbt1Zt+OLA6Fq9suIpIWD0OsnISlw==", "dev": true, "dependencies": { "locate-path": "^5.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true }, "node_modules/fsevents": { "version": "2.3.3", "resolved": "https://registry.npmjs.org/fsevents/-/fsevents-2.3.3.tgz", "integrity": "sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==", "dev": true, "hasInstallScript": true, "optional": true, "os": [ "darwin" ], "engines": { "node": "^8.16.0 || ^10.6.0 || >=11.0.0" } }, "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/gensync": { "version": "1.0.0-beta.2", "resolved": "https://registry.npmjs.org/gensync/-/gensync-1.0.0-beta.2.tgz", "integrity": "sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==", "dev": true, "engines": { "node": ">=6.9.0" } }, "node_modules/get-caller-file": { "version": "2.0.5", "resolved": "https://registry.npmjs.org/get-caller-file/-/get-caller-file-2.0.5.tgz", "integrity": "sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==", "dev": true, "engines": { "node": "6.* || 8.* || >= 10.*" } }, "node_modules/get-package-type": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/get-package-type/-/get-package-type-0.1.0.tgz", "integrity": "sha512-pjzuKtY64GYfWizNAJ0fr9VqttZkNiK2iS430LtIHzjBEr6bX8Am2zm4sW4Ro5wjWW5cAlRL1qAMTcXbjNAO2Q==", "dev": true, "engines": { "node": ">=8.0.0" } }, "node_modules/get-stream": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/get-stream/-/get-stream-6.0.1.tgz", "integrity": "sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Glob versions prior to v9 are no longer supported", "dev": true, "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/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/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "dependencies": { "function-bind": "^1.1.2" }, "engines": { "node": ">= 0.4" } }, "node_modules/html-escaper": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/html-escaper/-/html-escaper-2.0.2.tgz", "integrity": "sha512-H2iMtd0I4Mt5eYiapRdIDjp+XzelXQ0tFE4JS7YFwFevXXMmOp9myNrUvCg0D6ws8iqkRPBfKHgbwig1SmlLfg==", "dev": true }, "node_modules/human-signals": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/human-signals/-/human-signals-2.1.0.tgz", "integrity": "sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==", "dev": true, "engines": { "node": ">=10.17.0" } }, "node_modules/import-local": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/import-local/-/import-local-3.2.0.tgz", "integrity": "sha512-2SPlun1JUPWoM6t3F0dw0FkCF/jWY8kttcY4f599GLTSjh2OCuuhdTkJQsEcZzBqbXZGKMK2OqW1oZsjtf/gQA==", "dev": true, "dependencies": { "pkg-dir": "^4.2.0", "resolve-cwd": "^3.0.0" }, "bin": { "import-local-fixture": "fixtures/cli.js" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "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 }, "node_modules/is-arrayish": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/is-arrayish/-/is-arrayish-0.2.1.tgz", "integrity": "sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==", "dev": true }, "node_modules/is-core-module": { "version": "2.16.1", "resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.16.1.tgz", "integrity": "sha512-UfoeMA6fIJ8wTYFEUjelnaGI67v6+N7qXJEvQuIGa99l4xsCruSYOVSQ0uPANn4dAzm8lkYPaKLrrijLq7x23w==", "dev": true, "dependencies": { "hasown": "^2.0.2" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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-generator-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/is-generator-fn/-/is-generator-fn-2.1.0.tgz", "integrity": "sha512-cTIB4yPYL/Grw0EaSzASzg6bBy9gqCofvWN8okThAYIxKJZC+udlRAmGbM0XLeniEJSs8uEgHPGuHSe1XsOLSQ==", "dev": true, "engines": { "node": ">=6" } }, "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-stream": { "version": "2.0.1", "resolved": "https://registry.npmjs.org/is-stream/-/is-stream-2.0.1.tgz", "integrity": "sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true }, "node_modules/istanbul-lib-coverage": { "version": "3.2.2", "resolved": "https://registry.npmjs.org/istanbul-lib-coverage/-/istanbul-lib-coverage-3.2.2.tgz", "integrity": "sha512-O8dpsF+r0WV/8MNRKfnmrtCWhuKjxrq2w+jpzBL5UZKTi2LeVWnWOmWRxFlesJONmc+wLAGvKQZEOanko0LFTg==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/istanbul-lib-instrument": { "version": "6.0.3", "resolved": "https://registry.npmjs.org/istanbul-lib-instrument/-/istanbul-lib-instrument-6.0.3.tgz", "integrity": "sha512-Vtgk7L/R2JHyyGW07spoFlB8/lpjiOLTjMdms6AFMraYt3BaJauod/NGrfnVG/y4Ix1JEuMRPDPEj2ua+zz1/Q==", "dev": true, "dependencies": { "@babel/core": "^7.23.9", "@babel/parser": "^7.23.9", "@istanbuljs/schema": "^0.1.3", "istanbul-lib-coverage": "^3.2.0", "semver": "^7.5.4" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-instrument/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-report": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-report/-/istanbul-lib-report-3.0.1.tgz", "integrity": "sha512-GCfE1mtsHGOELCU8e/Z7YWzpmybrx/+dSTfLrvY8qRmaY6zXTKWn6WQIjaAFw069icm6GVMNkgu0NzI4iPZUNw==", "dev": true, "dependencies": { "istanbul-lib-coverage": "^3.0.0", "make-dir": "^4.0.0", "supports-color": "^7.1.0" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-lib-source-maps": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/istanbul-lib-source-maps/-/istanbul-lib-source-maps-4.0.1.tgz", "integrity": "sha512-n3s8EwkdFIJCG3BPKBYvskgXGoy88ARzvegkitk60NxRdwltLOTaH7CUiMRXvwYorl0Q712iEjcWB+fK/MrWVw==", "dev": true, "dependencies": { "debug": "^4.1.1", "istanbul-lib-coverage": "^3.0.0", "source-map": "^0.6.1" }, "engines": { "node": ">=10" } }, "node_modules/istanbul-reports": { "version": "3.1.7", "resolved": "https://registry.npmjs.org/istanbul-reports/-/istanbul-reports-3.1.7.tgz", "integrity": "sha512-BewmUXImeuRk2YY0PVbxgKAysvhRPUQE0h5QRM++nVWyubKGV0l8qQ5op8+B2DOmwSe63Jivj0BjkPQVf8fP5g==", "dev": true, "dependencies": { "html-escaper": "^2.0.0", "istanbul-lib-report": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/jake": { "version": "10.9.2", "resolved": "https://registry.npmjs.org/jake/-/jake-10.9.2.tgz", "integrity": "sha512-2P4SQ0HrLQ+fw6llpLnOaGAvN2Zu6778SJMrCUwns4fOoG9ayrTiZk3VV8sCPkVZF8ab0zksVpS8FDY5pRCNBA==", "dev": true, "dependencies": { "async": "^3.2.3", "chalk": "^4.0.2", "filelist": "^1.0.4", "minimatch": "^3.1.2" }, "bin": { "jake": "bin/cli.js" }, "engines": { "node": ">=10" } }, "node_modules/jest": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest/-/jest-29.7.0.tgz", "integrity": "sha512-NIy3oAFp9shda19hy4HK0HRTWKtPJmGdnvywu01nOqNC2vZg+Z+fvJDxpMQA88eb2I9EcafcdjYgsDthnYTvGw==", "dev": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/types": "^29.6.3", "import-local": "^3.0.2", "jest-cli": "^29.7.0" }, "bin": { "jest": "bin/jest.js" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { "node-notifier": { "optional": true } } }, "node_modules/jest-changed-files": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-changed-files/-/jest-changed-files-29.7.0.tgz", "integrity": "sha512-fEArFiwf1BpQ+4bXSprcDc3/x4HSzL4al2tozwVpDFpsxALjLYdyiIK4e5Vz66GQJIbXJ82+35PtysofptNX2w==", "dev": true, "dependencies": { "execa": "^5.0.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-circus": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-circus/-/jest-circus-29.7.0.tgz", "integrity": "sha512-3E1nCMgipcTkCocFwM90XXQab9bS+GMsjdpmPrlelaxwD93Ad8iVEjX/vvHPdLPnFf+L40u+5+iutRdA1N9myw==", "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/expect": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "co": "^4.6.0", "dedent": "^1.0.0", "is-generator-fn": "^2.0.0", "jest-each": "^29.7.0", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-runtime": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "p-limit": "^3.1.0", "pretty-format": "^29.7.0", "pure-rand": "^6.0.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-cli": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-cli/-/jest-cli-29.7.0.tgz", "integrity": "sha512-OVVobw2IubN/GSYsxETi+gOe7Ka59EFMR/twOU3Jb2GnKKeMGJB5SGUUrEz3SFVmJASUdZUzy83sLNNQ2gZslg==", "dev": true, "dependencies": { "@jest/core": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "chalk": "^4.0.0", "create-jest": "^29.7.0", "exit": "^0.1.2", "import-local": "^3.0.2", "jest-config": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "yargs": "^17.3.1" }, "bin": { "jest": "bin/jest.js" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "node-notifier": "^8.0.1 || ^9.0.0 || ^10.0.0" }, "peerDependenciesMeta": { "node-notifier": { "optional": true } } }, "node_modules/jest-config": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-config/-/jest-config-29.7.0.tgz", "integrity": "sha512-uXbpfeQ7R6TZBqI3/TxCU4q4ttk3u0PJeC+E0zbfSoSjq6bJ7buBPxzQPL0ifrkY4DNu4JUdk0ImlBUYi840eQ==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@jest/test-sequencer": "^29.7.0", "@jest/types": "^29.6.3", "babel-jest": "^29.7.0", "chalk": "^4.0.0", "ci-info": "^3.2.0", "deepmerge": "^4.2.2", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "jest-circus": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-get-type": "^29.6.3", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-runner": "^29.7.0", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "micromatch": "^4.0.4", "parse-json": "^5.2.0", "pretty-format": "^29.7.0", "slash": "^3.0.0", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "peerDependencies": { "@types/node": "*", "ts-node": ">=9.0.0" }, "peerDependenciesMeta": { "@types/node": { "optional": true }, "ts-node": { "optional": true } } }, "node_modules/jest-diff": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-diff/-/jest-diff-29.7.0.tgz", "integrity": "sha512-LMIgiIrhigmPrs03JHpxUh2yISK3vLFPkAodPeo0+BuF7wA2FoQbkEg1u8gBYBThncu7e1oEDUfIXVuTqLRUjw==", "dev": true, "dependencies": { "chalk": "^4.0.0", "diff-sequences": "^29.6.3", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-docblock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-docblock/-/jest-docblock-29.7.0.tgz", "integrity": "sha512-q617Auw3A612guyaFgsbFeYpNP5t2aoUNLwBUbc/0kD1R4t9ixDbyFTHd1nok4epoVFpr7PmeWHrhvuV3XaJ4g==", "dev": true, "dependencies": { "detect-newline": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-each": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-each/-/jest-each-29.7.0.tgz", "integrity": "sha512-gns+Er14+ZrEoC5fhOfYCY1LOHHr0TI+rQUHZS8Ttw2l7gl+80eHc/gFf2Ktkw0+SIACDTeWvpFcv3B04VembQ==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "jest-util": "^29.7.0", "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-environment-node": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-environment-node/-/jest-environment-node-29.7.0.tgz", "integrity": "sha512-DOSwCRqXirTOyheM+4d5YZOrWcdu0LNZ87ewUoywbcb2XR4wKgqiG8vNeYwhjFMbEkfju7wx2GYH0P2gevGvFw==", "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "jest-mock": "^29.7.0", "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-get-type": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-get-type/-/jest-get-type-29.6.3.tgz", "integrity": "sha512-zrteXnqYxfQh7l5FHyL38jL39di8H8rHoecLH3JNxH3BwOrBsNeabdap5e0I23lD4HHI8W5VFBZqG4Eaq5LNcw==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-haste-map": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-haste-map/-/jest-haste-map-29.7.0.tgz", "integrity": "sha512-fP8u2pyfqx0K1rGn1R9pyE0/KTn+G7PxktWidOBTqFPLYX0b9ksaMFkhK5vrS3DVun09pckLdlx90QthlW7AmA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/graceful-fs": "^4.1.3", "@types/node": "*", "anymatch": "^3.0.3", "fb-watchman": "^2.0.0", "graceful-fs": "^4.2.9", "jest-regex-util": "^29.6.3", "jest-util": "^29.7.0", "jest-worker": "^29.7.0", "micromatch": "^4.0.4", "walker": "^1.0.8" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" }, "optionalDependencies": { "fsevents": "^2.3.2" } }, "node_modules/jest-leak-detector": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-leak-detector/-/jest-leak-detector-29.7.0.tgz", "integrity": "sha512-kYA8IJcSYtST2BY9I+SMC32nDpBT3J2NvWJx8+JCuCdl/CR1I4EKUJROiP8XtCcxqgTTBGJNdbB1A8XRKbTetw==", "dev": true, "dependencies": { "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-matcher-utils": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-matcher-utils/-/jest-matcher-utils-29.7.0.tgz", "integrity": "sha512-sBkD+Xi9DtcChsI3L3u0+N0opgPYnCRPtGcQYrgXmR+hmt/fYfWAL0xRXYU8eWOdfuLgBe0YCW3AFtnRLagq/g==", "dev": true, "dependencies": { "chalk": "^4.0.0", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-message-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-message-util/-/jest-message-util-29.7.0.tgz", "integrity": "sha512-GBEV4GRADeP+qtB2+6u61stea8mGcOT4mCtrYISZwfu9/ISHFJ/5zOMXYbpBE9RsS5+Gb63DW4FgmnKJ79Kf6w==", "dev": true, "dependencies": { "@babel/code-frame": "^7.12.13", "@jest/types": "^29.6.3", "@types/stack-utils": "^2.0.0", "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "micromatch": "^4.0.4", "pretty-format": "^29.7.0", "slash": "^3.0.0", "stack-utils": "^2.0.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-mock": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-mock/-/jest-mock-29.7.0.tgz", "integrity": "sha512-ITOMZn+UkYS4ZFh83xYAOzWStloNzJFO2s8DWrE4lhtGD+AorgnbkiKERe4wQVBydIGPx059g6riW5Btp6Llnw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "jest-util": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-pnp-resolver": { "version": "1.2.3", "resolved": "https://registry.npmjs.org/jest-pnp-resolver/-/jest-pnp-resolver-1.2.3.tgz", "integrity": "sha512-+3NpwQEnRoIBtx4fyhblQDPgJI0H1IEIkX7ShLUjPGA7TtUTvI1oiKi3SR4oBR0hQhQR80l4WAe5RrXBwWMA8w==", "dev": true, "engines": { "node": ">=6" }, "peerDependencies": { "jest-resolve": "*" }, "peerDependenciesMeta": { "jest-resolve": { "optional": true } } }, "node_modules/jest-regex-util": { "version": "29.6.3", "resolved": "https://registry.npmjs.org/jest-regex-util/-/jest-regex-util-29.6.3.tgz", "integrity": "sha512-KJJBsRCyyLNWCNBOvZyRDnAIfUiRJ8v+hOBQYGn8gDyF3UegwiP4gwRR3/SDa42g1YbVycTidUF3rKjyLFDWbg==", "dev": true, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve/-/jest-resolve-29.7.0.tgz", "integrity": "sha512-IOVhZSrg+UvVAshDSDtHyFCCBUl/Q3AAJv8iZ6ZjnZ74xzvwuzLXid9IIIPgTnY62SJjfuupMKZsZQRsCvxEgA==", "dev": true, "dependencies": { "chalk": "^4.0.0", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-pnp-resolver": "^1.2.2", "jest-util": "^29.7.0", "jest-validate": "^29.7.0", "resolve": "^1.20.0", "resolve.exports": "^2.0.0", "slash": "^3.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-resolve-dependencies": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-resolve-dependencies/-/jest-resolve-dependencies-29.7.0.tgz", "integrity": "sha512-un0zD/6qxJ+S0et7WxeI3H5XSe9lTBBR7bOHCHXkKR6luG5mwDDlIzVQ0V5cZCuoTgEdcdwzTghYkTWfubi+nA==", "dev": true, "dependencies": { "jest-regex-util": "^29.6.3", "jest-snapshot": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runner": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runner/-/jest-runner-29.7.0.tgz", "integrity": "sha512-fsc4N6cPCAahybGBfTRcq5wFR6fpLznMg47sY5aDpsoejOcVYFb07AHuSnR0liMcPTgBsA3ZJL6kFOjPdoNipQ==", "dev": true, "dependencies": { "@jest/console": "^29.7.0", "@jest/environment": "^29.7.0", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "emittery": "^0.13.1", "graceful-fs": "^4.2.9", "jest-docblock": "^29.7.0", "jest-environment-node": "^29.7.0", "jest-haste-map": "^29.7.0", "jest-leak-detector": "^29.7.0", "jest-message-util": "^29.7.0", "jest-resolve": "^29.7.0", "jest-runtime": "^29.7.0", "jest-util": "^29.7.0", "jest-watcher": "^29.7.0", "jest-worker": "^29.7.0", "p-limit": "^3.1.0", "source-map-support": "0.5.13" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-runtime": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-runtime/-/jest-runtime-29.7.0.tgz", "integrity": "sha512-gUnLjgwdGqW7B4LvOIkbKs9WGbn+QLqRQQ9juC6HndeDiezIwhDP+mhMwHWCEcfQ5RUXa6OPnFF8BJh5xegwwQ==", "dev": true, "dependencies": { "@jest/environment": "^29.7.0", "@jest/fake-timers": "^29.7.0", "@jest/globals": "^29.7.0", "@jest/source-map": "^29.6.3", "@jest/test-result": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "cjs-module-lexer": "^1.0.0", "collect-v8-coverage": "^1.0.0", "glob": "^7.1.3", "graceful-fs": "^4.2.9", "jest-haste-map": "^29.7.0", "jest-message-util": "^29.7.0", "jest-mock": "^29.7.0", "jest-regex-util": "^29.6.3", "jest-resolve": "^29.7.0", "jest-snapshot": "^29.7.0", "jest-util": "^29.7.0", "slash": "^3.0.0", "strip-bom": "^4.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-snapshot": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-snapshot/-/jest-snapshot-29.7.0.tgz", "integrity": "sha512-Rm0BMWtxBcioHr1/OX5YCP8Uov4riHvKPknOGs804Zg9JGZgmIBkbtlxJC/7Z4msKYVbIJtfU+tKb8xlYNfdkw==", "dev": true, "dependencies": { "@babel/core": "^7.11.6", "@babel/generator": "^7.7.2", "@babel/plugin-syntax-jsx": "^7.7.2", "@babel/plugin-syntax-typescript": "^7.7.2", "@babel/types": "^7.3.3", "@jest/expect-utils": "^29.7.0", "@jest/transform": "^29.7.0", "@jest/types": "^29.6.3", "babel-preset-current-node-syntax": "^1.0.0", "chalk": "^4.0.0", "expect": "^29.7.0", "graceful-fs": "^4.2.9", "jest-diff": "^29.7.0", "jest-get-type": "^29.6.3", "jest-matcher-utils": "^29.7.0", "jest-message-util": "^29.7.0", "jest-util": "^29.7.0", "natural-compare": "^1.4.0", "pretty-format": "^29.7.0", "semver": "^7.5.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-snapshot/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/jest-util": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-util/-/jest-util-29.7.0.tgz", "integrity": "sha512-z6EbKajIpqGKU56y5KBUgy1dt1ihhQJgWzUlZHArA/+X2ad7Cb5iF+AK1EWVL/Bo7Rz9uurpqw6SiBCefUbCGA==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "@types/node": "*", "chalk": "^4.0.0", "ci-info": "^3.2.0", "graceful-fs": "^4.2.9", "picomatch": "^2.2.3" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-validate": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-validate/-/jest-validate-29.7.0.tgz", "integrity": "sha512-ZB7wHqaRGVw/9hST/OuFUReG7M8vKeq0/J2egIGLdvjHCmYqGARhzXmtgi+gVeZ5uXFF219aOc3Ls2yLg27tkw==", "dev": true, "dependencies": { "@jest/types": "^29.6.3", "camelcase": "^6.2.0", "chalk": "^4.0.0", "jest-get-type": "^29.6.3", "leven": "^3.1.0", "pretty-format": "^29.7.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-validate/node_modules/camelcase": { "version": "6.3.0", "resolved": "https://registry.npmjs.org/camelcase/-/camelcase-6.3.0.tgz", "integrity": "sha512-Gmy6FhYlCY7uOElZUSbxo2UCDH8owEk996gkbrpsgGtrJLM3J7jGxl9Ic7Qwwj4ivOE5AWZWRMecDdF7hqGjFA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/jest-watcher": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-watcher/-/jest-watcher-29.7.0.tgz", "integrity": "sha512-49Fg7WXkU3Vl2h6LbLtMQ/HyB6rXSIX7SqvBLQmssRBGN9I0PNvPmAmCWSOY6SOvrjhI/F7/bGAv9RtnsPA03g==", "dev": true, "dependencies": { "@jest/test-result": "^29.7.0", "@jest/types": "^29.6.3", "@types/node": "*", "ansi-escapes": "^4.2.1", "chalk": "^4.0.0", "emittery": "^0.13.1", "jest-util": "^29.7.0", "string-length": "^4.0.1" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/jest-worker/-/jest-worker-29.7.0.tgz", "integrity": "sha512-eIz2msL/EzL9UFTFFx7jBTkeZfku0yUAyZZZmJ93H2TYEiroIx2PQjEXcwYtYl8zXCxb+PAmA2hLIt/6ZEkPHw==", "dev": true, "dependencies": { "@types/node": "*", "jest-util": "^29.7.0", "merge-stream": "^2.0.0", "supports-color": "^8.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/jest-worker/node_modules/supports-color": { "version": "8.1.1", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-8.1.1.tgz", "integrity": "sha512-MpUEN2OodtUzxvKQl72cUF7RQ5EiHsGvSsVG0ia9c5RbWGL2CI4C7EpPS8UTBIplnlzZiNuV56w+FuNxy3ty2Q==", "dev": true, "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/supports-color?sponsor=1" } }, "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": "3.14.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-3.14.1.tgz", "integrity": "sha512-okMH7OXXJ7YrN9Ok3/SXrnu4iX9yOk+25nqX4imS2npuvTYDmo/QEZoqwZkYaIDk3jVvBOTOIEgEhaLOynBS9g==", "dev": true, "dependencies": { "argparse": "^1.0.7", "esprima": "^4.0.0" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/jsesc": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/jsesc/-/jsesc-3.1.0.tgz", "integrity": "sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==", "dev": true, "bin": { "jsesc": "bin/jsesc" }, "engines": { "node": ">=6" } }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", "integrity": "sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==", "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/kleur": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/kleur/-/kleur-3.0.3.tgz", "integrity": "sha512-eTIzlVOSUR+JxdDFepEYcBMtZ9Qqdef+rnzWdRZuMbOywu5tO2w2N7rqjoANZ5k9vywhL6Br1VRjUIgTQx4E8w==", "dev": true, "engines": { "node": ">=6" } }, "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/lines-and-columns": { "version": "1.2.4", "resolved": "https://registry.npmjs.org/lines-and-columns/-/lines-and-columns-1.2.4.tgz", "integrity": "sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==", "dev": true }, "node_modules/locate-path": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-5.0.0.tgz", "integrity": "sha512-t7hw9pI+WvuwNJXwk5zVHpyhIqzg2qTlklJOf0mVxGSbe3Fp2VieZcduNYjaLDoy6p9uGpQEGWG87WpMKlNq8g==", "dev": true, "dependencies": { "p-locate": "^4.1.0" }, "engines": { "node": ">=8" } }, "node_modules/lodash.memoize": { "version": "4.1.2", "resolved": "https://registry.npmjs.org/lodash.memoize/-/lodash.memoize-4.1.2.tgz", "integrity": "sha512-t7j+NzmgnQzTAYXcsHYLgimltOV1MXHtlOWf6GjL9Kj8GK5FInw5JotxvbOs+IvV1/Dzo04/fCGfLVs7aXb4Ag==", "dev": true }, "node_modules/lru-cache": { "version": "5.1.1", "resolved": "https://registry.npmjs.org/lru-cache/-/lru-cache-5.1.1.tgz", "integrity": "sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==", "dev": true, "dependencies": { "yallist": "^3.0.2" } }, "node_modules/make-dir": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/make-dir/-/make-dir-4.0.0.tgz", "integrity": "sha512-hXdUTZYIVOt1Ex//jAQi+wTZZpUpwBj/0QsOzqegb3rGMMeJiSEu5xLHnYfBrRV4RH2+OCSOO95Is/7x1WJ4bw==", "dev": true, "dependencies": { "semver": "^7.5.3" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/make-dir/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/make-error": { "version": "1.3.6", "resolved": "https://registry.npmjs.org/make-error/-/make-error-1.3.6.tgz", "integrity": "sha512-s8UhlNe7vPKomQhC1qFelMokr/Sc3AgNbso3n74mVPA5LTZwkB9NlXf4XPamLxJE8h0gh73rM94xvwRT2CVInw==", "dev": true }, "node_modules/makeerror": { "version": "1.0.12", "resolved": "https://registry.npmjs.org/makeerror/-/makeerror-1.0.12.tgz", "integrity": "sha512-JmqCvUhmt43madlpFzG4BQzG2Z3m6tvQDNKdClZnO3VbIudJYmxsT0FNJMeiB2+JTSlTQTSbU8QdesVmwJcmLg==", "dev": true, "dependencies": { "tmpl": "1.0.5" } }, "node_modules/merge-stream": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/merge-stream/-/merge-stream-2.0.0.tgz", "integrity": "sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==", "dev": true }, "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/mimic-fn": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/mimic-fn/-/mimic-fn-2.1.0.tgz", "integrity": "sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", "dev": true, "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/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/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true }, "node_modules/node-int64": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/node-int64/-/node-int64-0.4.0.tgz", "integrity": "sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==", "dev": true }, "node_modules/node-releases": { "version": "2.0.19", "resolved": "https://registry.npmjs.org/node-releases/-/node-releases-2.0.19.tgz", "integrity": "sha512-xxOWJsBKtzAq7DY0J+DTzuz58K8e7sJbdgwkbMWQe8UYB6ekmsQ45q0M/tJDsGaZmbC+l7n57UV8Hl5tHxO9uw==", "dev": true }, "node_modules/normalize-path": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/normalize-path/-/normalize-path-3.0.0.tgz", "integrity": "sha512-6eZs5Ls3WtCisHWp9S2GUy8dqkpGi4BVSz3GaqiE6ezub0512ESztXUwUB6C6IKbQkY2Pnb/mD4WYojCRwcwLA==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/npm-run-path": { "version": "4.0.1", "resolved": "https://registry.npmjs.org/npm-run-path/-/npm-run-path-4.0.1.tgz", "integrity": "sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==", "dev": true, "dependencies": { "path-key": "^3.0.0" }, "engines": { "node": ">=8" } }, "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, "dependencies": { "wrappy": "1" } }, "node_modules/onetime": { "version": "5.1.2", "resolved": "https://registry.npmjs.org/onetime/-/onetime-5.1.2.tgz", "integrity": "sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==", "dev": true, "dependencies": { "mimic-fn": "^2.1.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-4.1.0.tgz", "integrity": "sha512-R79ZZ/0wAxKGu3oYMlz8jy/kbhsNrS7SKZ7PxEHBgJ5+F2mtFW2fK2cOtBh1cHYkQsbzFV7I+EoRKe6Yt0oK7A==", "dev": true, "dependencies": { "p-limit": "^2.2.0" }, "engines": { "node": ">=8" } }, "node_modules/p-locate/node_modules/p-limit": { "version": "2.3.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-2.3.0.tgz", "integrity": "sha512-//88mFWSJx8lxCzwdAABTJL2MyWB12+eIY7MDL2SqLmAkeKU9qxRvWuSyTjm3FUmpBEMuFfckAIqEaVGUDxb6w==", "dev": true, "dependencies": { "p-try": "^2.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-try": { "version": "2.2.0", "resolved": "https://registry.npmjs.org/p-try/-/p-try-2.2.0.tgz", "integrity": "sha512-R4nPAVTAU0B9D35/Gk3uJf/7XYbQcyohSKdvAxIRSNghFl4e71hVoGnBNQz9cWaXxO2I10KTC+3jMdvvoKw6dQ==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/parse-json": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/parse-json/-/parse-json-5.2.0.tgz", "integrity": "sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==", "dev": true, "dependencies": { "@babel/code-frame": "^7.0.0", "error-ex": "^1.3.1", "json-parse-even-better-errors": "^2.3.0", "lines-and-columns": "^1.1.6" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/path-parse": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/path-parse/-/path-parse-1.0.7.tgz", "integrity": "sha512-LDJzPVEEEPR+y48z93A0Ed0yXb8pAByGWo/k5YYdYgpY2/2EsOsksJrq7lOHxryrVOn1ejG6oAp8ahvOIQD8sw==", "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.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "node_modules/pirates": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/pirates/-/pirates-4.0.7.tgz", "integrity": "sha512-TfySrs/5nm8fQJDcBDuUng3VOUKsd7S+zqvbOTiGXHfxX4wK31ard+hoNuvkicM/2YFzlpDgABOevKSsB4G/FA==", "dev": true, "engines": { "node": ">= 6" } }, "node_modules/pkg-dir": { "version": "4.2.0", "resolved": "https://registry.npmjs.org/pkg-dir/-/pkg-dir-4.2.0.tgz", "integrity": "sha512-HRDzbaKjC+AOWVXxAU/x54COGeIv9eb+6CkDSQoNTt4XyWoIJvuPsXizxu/Fr23EiekbtZwmh1IcIG/l/a10GQ==", "dev": true, "dependencies": { "find-up": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/pretty-format": { "version": "29.7.0", "resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-29.7.0.tgz", "integrity": "sha512-Pdlw/oPxN+aXdmM9R00JVC9WVFoCLTKJvDVLgmJ+qAffBMxsV85l/Lu7sNx4zSzPyoL2euImuEwHhOXdEgNFZQ==", "dev": true, "dependencies": { "@jest/schemas": "^29.6.3", "ansi-styles": "^5.0.0", "react-is": "^18.0.0" }, "engines": { "node": "^14.15.0 || ^16.10.0 || >=18.0.0" } }, "node_modules/pretty-format/node_modules/ansi-styles": { "version": "5.2.0", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-5.2.0.tgz", "integrity": "sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/prompts": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/prompts/-/prompts-2.4.2.tgz", "integrity": "sha512-NxNv/kLguCA7p3jE8oL2aEBsrJWgAakBpgmgK6lpPWV+WuOmY6r2/zbAVnP+T8bQlA0nzHXSJSJW0Hq7ylaD2Q==", "dev": true, "dependencies": { "kleur": "^3.0.3", "sisteransi": "^1.0.5" }, "engines": { "node": ">= 6" } }, "node_modules/pure-rand": { "version": "6.1.0", "resolved": "https://registry.npmjs.org/pure-rand/-/pure-rand-6.1.0.tgz", "integrity": "sha512-bVWawvoZoBYpp6yIoQtQXHZjmz35RSVHnUOTefl8Vcjr8snTPY1wnpSPMWekcFwbxI6gtmT7rSYPFvz71ldiOA==", "dev": true, "funding": [ { "type": "individual", "url": "https://github.com/sponsors/dubzzz" }, { "type": "opencollective", "url": "https://opencollective.com/fast-check" } ] }, "node_modules/react-is": { "version": "18.3.1", "resolved": "https://registry.npmjs.org/react-is/-/react-is-18.3.1.tgz", "integrity": "sha512-/LLMVyas0ljjAtoYiPqYiL8VWXzUUdThrmU5+n20DZv+a+ClRoevUzw5JxU+Ieh5/c87ytoTBV9G1FiKfNJdmg==", "dev": true }, "node_modules/require-directory": { "version": "2.1.1", "resolved": "https://registry.npmjs.org/require-directory/-/require-directory-2.1.1.tgz", "integrity": "sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/resolve": { "version": "1.22.10", "resolved": "https://registry.npmjs.org/resolve/-/resolve-1.22.10.tgz", "integrity": "sha512-NPRy+/ncIMeDlTAsuqwKIiferiawhefFJtkNSW0qZJEqMEb+qBt/77B/jGeeek+F0uOeN05CDa6HXbbIgtVX4w==", "dev": true, "dependencies": { "is-core-module": "^2.16.0", "path-parse": "^1.0.7", "supports-preserve-symlinks-flag": "^1.0.0" }, "bin": { "resolve": "bin/resolve" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/resolve-cwd": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/resolve-cwd/-/resolve-cwd-3.0.0.tgz", "integrity": "sha512-OrZaX2Mb+rJCpH/6CpSqt9xFVpN++x01XnN2ie9g6P5/3xelLAkXWVADpdz1IHD/KFfEXyE6V0U01OQ3UO2rEg==", "dev": true, "dependencies": { "resolve-from": "^5.0.0" }, "engines": { "node": ">=8" } }, "node_modules/resolve-from": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-5.0.0.tgz", "integrity": "sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/resolve.exports": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/resolve.exports/-/resolve.exports-2.0.3.tgz", "integrity": "sha512-OcXjMsGdhL4XnbShKpAcSqPMzQoYkYyhbEaeSko47MjRP9NfEQMhZkXL1DoFlt9LWQn4YttrdnV6X2OiyzBi+A==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/semver": { "version": "6.3.1", "resolved": "https://registry.npmjs.org/semver/-/semver-6.3.1.tgz", "integrity": "sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==", "dev": true, "bin": { "semver": "bin/semver.js" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/signal-exit": { "version": "3.0.7", "resolved": "https://registry.npmjs.org/signal-exit/-/signal-exit-3.0.7.tgz", "integrity": "sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==", "dev": true }, "node_modules/sisteransi": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/sisteransi/-/sisteransi-1.0.5.tgz", "integrity": "sha512-bLGGlR1QxBcynn2d5YmDX4MGjlZvy2MRBDRNHLJ8VI6l6+9FUiyTFNJ0IveOSP0bcXgVDPRcfGqA0pjaqUpfVg==", "dev": true }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/source-map": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz", "integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==", "dev": true, "engines": { "node": ">=0.10.0" } }, "node_modules/source-map-support": { "version": "0.5.13", "resolved": "https://registry.npmjs.org/source-map-support/-/source-map-support-0.5.13.tgz", "integrity": "sha512-SHSKFHadjVA5oR4PPqhtAVdcBWwRYVd6g6cAXnIbRiIwc2EhPrTuKUBdSLvlEKyIP3GCf89fltvcZiP9MMFA1w==", "dev": true, "dependencies": { "buffer-from": "^1.0.0", "source-map": "^0.6.0" } }, "node_modules/sprintf-js": { "version": "1.0.3", "resolved": "https://registry.npmjs.org/sprintf-js/-/sprintf-js-1.0.3.tgz", "integrity": "sha512-D9cPgkvLlV3t3IzL0D0YLvGA9Ahk4PcvVwUbN0dSGr1aP0Nrt4AEnTUbuGvquEC0mA64Gqt1fzirlRs5ibXx8g==", "dev": true }, "node_modules/stack-utils": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/stack-utils/-/stack-utils-2.0.6.tgz", "integrity": "sha512-XlkWvfIm6RmsWtNJx+uqtKLS8eqFbxUg0ZzLXqY0caEy9l7hruX8IpiDnjsLavoBgqCCR71TqWO8MaXYheJ3RQ==", "dev": true, "dependencies": { "escape-string-regexp": "^2.0.0" }, "engines": { "node": ">=10" } }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", "integrity": "sha512-+l6rNN5fYHNhZZy41RXsYptCjA2Igmq4EG7kZAYFQI1E1VTXarr6ZPXBg6eq7Y6eK4FEhY6AJlyuFIb/v/S0VQ==", "dev": true, "dependencies": { "char-regex": "^1.0.2", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" } }, "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/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-bom": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/strip-bom/-/strip-bom-4.0.0.tgz", "integrity": "sha512-3xurFv5tEgii33Zi8Jtp55wEIILR9eh34FAW00PZf+JnSsTmV/ioewSgQl97JHvgjoRGwPShsWm+IdrxB35d0w==", "dev": true, "engines": { "node": ">=8" } }, "node_modules/strip-final-newline": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/strip-final-newline/-/strip-final-newline-2.0.0.tgz", "integrity": "sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==", "dev": true, "engines": { "node": ">=6" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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-preserve-symlinks-flag": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/supports-preserve-symlinks-flag/-/supports-preserve-symlinks-flag-1.0.0.tgz", "integrity": "sha512-ot0WnXS9fgdkgIcePe6RHNk1WA8+muPa6cSjeR3V8K27q9BB1rTE3R1p7Hv0z1ZyAc8s6Vvv8DIyWf681MAt0w==", "dev": true, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/test-exclude": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/test-exclude/-/test-exclude-6.0.0.tgz", "integrity": "sha512-cAGWPIyOHU6zlmg88jwm7VRyXnMN7iV68OGAbYDk/Mh/xC/pzVPlQtY6ngoIH/5/tciuhGfvESU8GrHrcxD56w==", "dev": true, "dependencies": { "@istanbuljs/schema": "^0.1.2", "glob": "^7.1.4", "minimatch": "^3.0.4" }, "engines": { "node": ">=8" } }, "node_modules/tmpl": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/tmpl/-/tmpl-1.0.5.tgz", "integrity": "sha512-3f0uOEAQwIqGuWW2MVzYg8fV/QNnc/IpuJNG837rLuczAaLVHslWHZQj4IGiEl5Hs3kkbhwL9Ab7Hrsmuj+Smw==", "dev": true }, "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/ts-jest": { "version": "29.4.0", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.4.0.tgz", "integrity": "sha512-d423TJMnJGu80/eSgfQ5w/R+0zFJvdtTxwtF9KzFFunOpSeD+79lHJQIiAhluJoyGRbvj9NZJsl9WjCUo0ND7Q==", "dev": true, "dependencies": { "bs-logger": "^0.2.6", "ejs": "^3.1.10", "fast-json-stable-stringify": "^2.1.0", "json5": "^2.2.3", "lodash.memoize": "^4.1.2", "make-error": "^1.3.6", "semver": "^7.7.2", "type-fest": "^4.41.0", "yargs-parser": "^21.1.1" }, "bin": { "ts-jest": "cli.js" }, "engines": { "node": "^14.15.0 || ^16.10.0 || ^18.0.0 || >=20.0.0" }, "peerDependencies": { "@babel/core": ">=7.0.0-beta.0 <8", "@jest/transform": "^29.0.0 || ^30.0.0", "@jest/types": "^29.0.0 || ^30.0.0", "babel-jest": "^29.0.0 || ^30.0.0", "jest": "^29.0.0 || ^30.0.0", "jest-util": "^29.0.0 || ^30.0.0", "typescript": ">=4.3 <6" }, "peerDependenciesMeta": { "@babel/core": { "optional": true }, "@jest/transform": { "optional": true }, "@jest/types": { "optional": true }, "babel-jest": { "optional": true }, "esbuild": { "optional": true }, "jest-util": { "optional": true } } }, "node_modules/ts-jest/node_modules/semver": { "version": "7.7.2", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.2.tgz", "integrity": "sha512-RF0Fw+rO5AMf9MAyaRXI4AV0Ulj5lMHqVxxdSgiVbixSCXoEmmX/jk0CuJw4+3SqroYO9VoUh+HcuJivvtJemA==", "dev": true, "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/ts-jest/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/type-detect": { "version": "4.0.8", "resolved": "https://registry.npmjs.org/type-detect/-/type-detect-4.0.8.tgz", "integrity": "sha512-0fr/mIH1dlO+x7TlcMy+bIDqKPsw/70tVyeHW787goQjhmqaZe10uwLujubK9q9Lg6Fiho1KUKDYz0Z7k7g5/g==", "dev": true, "engines": { "node": ">=4" } }, "node_modules/type-fest": { "version": "0.21.3", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.21.3.tgz", "integrity": "sha512-t0rzBq87m3fVcduHDUFhKmyyX+9eo6WQjZvf51Ea/M0Q7+T374Jp1aUiyUl0GKxp8M/OETVHSDvmkyPgvX+X2w==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/typescript": { "version": "4.9.5", "resolved": "https://registry.npmjs.org/typescript/-/typescript-4.9.5.tgz", "integrity": "sha512-1FXk9E2Hm+QzZQ7z+McJiHL4NW1F2EzMu9Nq9i3zAaGqibafqYwCVU6WyWAuyQRRzOlxou8xZSyXLEN8oKj24g==", "dev": true, "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=4.2.0" } }, "node_modules/update-browserslist-db": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/update-browserslist-db/-/update-browserslist-db-1.1.3.tgz", "integrity": "sha512-UxhIZQ+QInVdunkDAaiazvvT/+fXL5Osr0JZlJulepYu6Jd7qJtDZjlur0emRlT71EN3ScPoE7gvsuIKKNavKw==", "dev": true, "funding": [ { "type": "opencollective", "url": "https://opencollective.com/browserslist" }, { "type": "tidelift", "url": "https://tidelift.com/funding/github/npm/browserslist" }, { "type": "github", "url": "https://github.com/sponsors/ai" } ], "dependencies": { "escalade": "^3.2.0", "picocolors": "^1.1.1" }, "bin": { "update-browserslist-db": "cli.js" }, "peerDependencies": { "browserslist": ">= 4.21.0" } }, "node_modules/v8-to-istanbul": { "version": "9.3.0", "resolved": "https://registry.npmjs.org/v8-to-istanbul/-/v8-to-istanbul-9.3.0.tgz", "integrity": "sha512-kiGUalWN+rgBJ/1OHZsBtU4rXZOfj/7rKQxULKlIzwzQSvMJUUNgPwJEEh7gU6xEVxC0ahoOBvN2YI8GH6FNgA==", "dev": true, "dependencies": { "@jridgewell/trace-mapping": "^0.3.12", "@types/istanbul-lib-coverage": "^2.0.1", "convert-source-map": "^2.0.0" }, "engines": { "node": ">=10.12.0" } }, "node_modules/walker": { "version": "1.0.8", "resolved": "https://registry.npmjs.org/walker/-/walker-1.0.8.tgz", "integrity": "sha512-ts/8E8l5b7kY0vlWLewOkDXMmPdLcVV4GmOQLyxuSswIJsweeFZtAsMF7k1Nszz+TYBQrlYRmzOnr398y1JemQ==", "dev": true, "dependencies": { "makeerror": "1.0.12" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", "integrity": "sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==", "dev": true, "dependencies": { "ansi-styles": "^4.0.0", "string-width": "^4.1.0", "strip-ansi": "^6.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/chalk/wrap-ansi?sponsor=1" } }, "node_modules/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 }, "node_modules/write-file-atomic": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/write-file-atomic/-/write-file-atomic-4.0.2.tgz", "integrity": "sha512-7KxauUdBmSdWnmpaGFg+ppNjKF8uNLry8LyzjauQDOVONfFLNKrKvQOxZ/VuTIcS/gge/YNahf5RIIQWTSarlg==", "dev": true, "dependencies": { "imurmurhash": "^0.1.4", "signal-exit": "^3.0.7" }, "engines": { "node": "^12.13.0 || ^14.15.0 || >=16.0.0" } }, "node_modules/y18n": { "version": "5.0.8", "resolved": "https://registry.npmjs.org/y18n/-/y18n-5.0.8.tgz", "integrity": "sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==", "dev": true, "engines": { "node": ">=10" } }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", "integrity": "sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==", "dev": true }, "node_modules/yargs": { "version": "17.7.2", "resolved": "https://registry.npmjs.org/yargs/-/yargs-17.7.2.tgz", "integrity": "sha512-7dSzzRQ++CKnNI/krKnYRV7JKKPUXMEh61soaHKg9mrWEhzFWhFnxPxGl+69cD1Ou63C13NUPCnmIcrvqCuM6w==", "dev": true, "dependencies": { "cliui": "^8.0.1", "escalade": "^3.1.1", "get-caller-file": "^2.0.5", "require-directory": "^2.1.1", "string-width": "^4.2.3", "y18n": "^5.0.5", "yargs-parser": "^21.1.1" }, "engines": { "node": ">=12" } }, "node_modules/yargs-parser": { "version": "21.1.1", "resolved": "https://registry.npmjs.org/yargs-parser/-/yargs-parser-21.1.1.tgz", "integrity": "sha512-tVpsJW7DdjecAiFpbIB1e3qxIQsE6NoPc5/eTdrbbIC4h0LVsWhnoa3g+m2HclBIujHzsxZ4VJVA+GUuc2/LBw==", "dev": true, "engines": { "node": ">=12" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } } } debian-lsp-0.1.4/coc-debian/package.json000064400000000000000000000027271046102023000161260ustar 00000000000000{ "name": "coc-debian", "version": "0.1.0", "description": "Debian language server support for coc.nvim (control, copyright, watch, changelog, tests/control, source/format, upstream/metadata)", "main": "lib/index.js", "engines": { "coc": "^0.0.80" }, "keywords": [ "coc.nvim", "debian", "control", "copyright", "watch", "changelog", "deb822" ], "author": "Jelmer Vernooij", "license": "Apache-2.0", "repository": { "type": "git", "url": "https://github.com/jelmer/debian-lsp" }, "homepage": "https://github.com/jelmer/debian-lsp#readme", "bugs": { "url": "https://github.com/jelmer/debian-lsp/issues" }, "activationEvents": [ "*" ], "contributes": { "configuration": { "type": "object", "title": "coc-debian configuration", "properties": { "debian.enable": { "type": "boolean", "default": true, "description": "Enable debian language server" }, "debian.serverPath": { "type": "string", "default": "debian-lsp", "description": "Path to debian-lsp executable" } } } }, "scripts": { "build": "tsc -p .", "watch": "tsc -p . -w", "test": "jest", "test:watch": "jest --watch" }, "devDependencies": { "@types/node": "^12.12.0", "@types/jest": "^29.0.0", "coc.nvim": "^0.0.82", "jest": "^29.0.0", "ts-jest": "^29.0.0", "typescript": "^4.9.0" } }debian-lsp-0.1.4/coc-debian/src/index.ts000064400000000000000000000030541046102023000161000ustar 00000000000000import { ExtensionContext, LanguageClient, LanguageClientOptions, ServerOptions, services, workspace } from 'coc.nvim'; export async function activate(context: ExtensionContext): Promise { const config = workspace.getConfiguration('debian'); const isEnable = config.get('enable', true); if (!isEnable) { return; } const serverPath = config.get('serverPath', 'debian-lsp'); const serverOptions: ServerOptions = { command: serverPath, args: [] }; const clientOptions: LanguageClientOptions = { documentSelector: [ { scheme: 'file', pattern: '**/debian/control' }, { scheme: 'file', pattern: '**/control' }, { scheme: 'file', pattern: '**/debian/copyright' }, { scheme: 'file', pattern: '**/copyright' }, { scheme: 'file', pattern: '**/debian/watch' }, { scheme: 'file', pattern: '**/watch' }, { scheme: 'file', pattern: '**/debian/tests/control' }, { scheme: 'file', pattern: '**/debian/changelog' }, { scheme: 'file', pattern: '**/changelog' }, { scheme: 'file', pattern: '**/debian/source/format' }, { scheme: 'file', pattern: '**/debian/upstream/metadata' } ], synchronize: { fileEvents: workspace.createFileSystemWatcher('**/debian/{control,copyright,watch,changelog,tests/control,source/format,upstream/metadata}') } }; const client = new LanguageClient( 'debian', 'Debian Language Server', serverOptions, clientOptions ); context.subscriptions.push(services.registLanguageClient(client)); }debian-lsp-0.1.4/coc-debian/src/test.ts000064400000000000000000000020621046102023000157460ustar 00000000000000import { workspace, ExtensionContext } from 'coc.nvim'; describe('coc-debian', () => { it('should register debian language server', async () => { const config = workspace.getConfiguration('debian'); expect(config.get('enable')).toBe(true); expect(config.get('serverPath')).toBe('debian-lsp'); }); it('should activate on debian-control language', () => { const activationEvents = require('../package.json').activationEvents; expect(activationEvents).toContain('onLanguage:debian-control'); }); it('should have correct configuration properties', () => { const packageJson = require('../package.json'); const config = packageJson.contributes.configuration.properties; expect(config['debian.enable']).toBeDefined(); expect(config['debian.enable'].type).toBe('boolean'); expect(config['debian.enable'].default).toBe(true); expect(config['debian.serverPath']).toBeDefined(); expect(config['debian.serverPath'].type).toBe('string'); expect(config['debian.serverPath'].default).toBe('debian-lsp'); }); });debian-lsp-0.1.4/coc-debian/tsconfig.json000064400000000000000000000004551046102023000163430ustar 00000000000000{ "compilerOptions": { "target": "es2020", "module": "commonjs", "outDir": "lib", "rootDir": "src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true }, "include": ["src/**/*"], "exclude": ["node_modules"] }debian-lsp-0.1.4/src/architecture.rs000064400000000000000000000023041046102023000154620ustar 00000000000000use std::sync::Arc; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; use tokio::sync::RwLock; /// Thread-safe shared architecture list. pub type SharedArchitectureList = Arc>>; /// Create a new empty shared architecture list. pub fn new_shared_list() -> SharedArchitectureList { Arc::new(RwLock::new(Vec::new())) } /// Stream architecture names from `dpkg-architecture -L` into the shared list. /// /// Each architecture name is inserted (in sorted order) as soon as it is /// read, so completions are available immediately while still loading. pub async fn stream_into(list: &SharedArchitectureList) { let Ok(mut child) = Command::new("dpkg-architecture") .arg("-L") .stdout(std::process::Stdio::piped()) .spawn() else { return; }; let Some(stdout) = child.stdout.take() else { return; }; let mut lines = BufReader::new(stdout).lines(); while let Ok(Some(line)) = lines.next_line().await { if !line.is_empty() { let mut arches = list.write().await; let pos = arches.binary_search(&line).unwrap_or_else(|p| p); arches.insert(pos, line); } } } debian-lsp-0.1.4/src/changelog/actions.rs000064400000000000000000000133111046102023000163670ustar 00000000000000use chrono::Local; use std::env; /// Determine the appropriate distribution to use when marking an entry for upload pub fn get_target_distribution(changelog: &debian_changelog::ChangeLog) -> String { // Look for the most recent released entry (not UNRELEASED) changelog .iter() .skip(1) // Skip the first entry .find_map(|entry| { entry.distributions().and_then(|dists| { if !dists.is_empty() && dists[0] != "UNRELEASED" { Some(dists[0].clone()) } else { None } }) }) .unwrap_or_else(|| "unstable".to_string()) } /// Generates a new changelog entry text with incremented debian revision pub fn generate_new_changelog_entry( current_changelog: &debian_changelog::ChangeLog, ) -> Result { // Get the first (most recent) entry let entries: Vec<_> = current_changelog.iter().collect(); let first_entry = entries.first().ok_or("No entries in changelog")?; // Get current version and increment debian revision let current_version = first_entry.version().ok_or("No version found")?; let mut new_version = current_version.clone(); new_version.increment_debian(); // Get package name let package = first_entry.package().ok_or("No package name found")?; // Always use UNRELEASED for new entries let distribution = "UNRELEASED"; // Get maintainer info from environment or current entry let (maintainer_name, maintainer_email) = get_maintainer_info(first_entry); // Format timestamp let now = Local::now(); let timestamp = now.format("%a, %d %b %Y %H:%M:%S %z").to_string(); // Generate the new entry let entry = format!( "{} ({}) {}; urgency=medium\n\n * \n\n -- {} <{}> {}\n\n", package, new_version, distribution, maintainer_name, maintainer_email, timestamp ); Ok(entry) } /// Get maintainer info from environment variables or current entry fn get_maintainer_info(current_entry: &debian_changelog::Entry) -> (String, String) { // Try DEBFULLNAME and DEBEMAIL environment variables first let env_name = env::var("DEBFULLNAME").ok(); let env_email = env::var("DEBEMAIL").ok(); if let (Some(name), Some(email)) = (env_name, env_email) { return (name, email); } // Fall back to current entry's maintainer let name = current_entry .maintainer() .map(|s| s.to_string()) .unwrap_or_else(|| "Unknown".to_string()); let email = current_entry .email() .map(|s| s.to_string()) .unwrap_or_else(|| "unknown@example.com".to_string()); (name, email) } #[cfg(test)] mod tests { use super::*; #[test] fn test_generate_new_changelog_entry() { let changelog_text = r#"foo (1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_text); let changelog = parsed.tree(); // Set environment variables for predictable maintainer info env::set_var("DEBFULLNAME", "Test User"); env::set_var("DEBEMAIL", "test@example.com"); let new_entry = generate_new_changelog_entry(&changelog).unwrap(); // Parse the generated entry to verify structure let lines: Vec<&str> = new_entry.lines().collect(); // Check the header line has correct package and version with UNRELEASED assert_eq!(lines[0], "foo (1.0-2) UNRELEASED; urgency=medium"); // Check empty line after header assert_eq!(lines[1], ""); // Check bullet point line assert_eq!(lines[2], " * "); // Check empty line before signature assert_eq!(lines[3], ""); // Check signature line starts correctly assert!(lines[4].starts_with(" -- Test User ")); // Clean up environment env::remove_var("DEBFULLNAME"); env::remove_var("DEBEMAIL"); } #[test] fn test_get_target_distribution_from_previous_entry() { let changelog_text = r#"foo (1.0-2) UNRELEASED; urgency=medium * New changes. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 foo (1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_text); let changelog = parsed.tree(); let target = get_target_distribution(&changelog); assert_eq!(target, "unstable"); } #[test] fn test_get_target_distribution_defaults_to_unstable() { let changelog_text = r#"foo (1.0-1) UNRELEASED; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_text); let changelog = parsed.tree(); let target = get_target_distribution(&changelog); assert_eq!(target, "unstable"); } #[test] fn test_get_target_distribution_skips_unreleased_entries() { let changelog_text = r#"foo (1.0-3) UNRELEASED; urgency=medium * Latest changes. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 foo (1.0-2) UNRELEASED; urgency=medium * More changes. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 foo (1.0-1) experimental; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_text); let changelog = parsed.tree(); let target = get_target_distribution(&changelog); assert_eq!(target, "experimental"); } } debian-lsp-0.1.4/src/changelog/completion.rs000064400000000000000000000311551046102023000171060ustar 00000000000000use rowan::ast::AstNode; use text_size::{TextRange, TextSize}; use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Documentation, Position}; use super::fields::{get_debian_distributions, URGENCY_LEVELS}; #[derive(Debug, Clone, PartialEq, Eq)] enum CursorContext { DistributionValue { value_prefix: String }, UrgencyValue { value_prefix: String }, } /// Get completion items for a changelog file at the given cursor position. /// /// Uses changelog CST context to return only relevant value completions: /// distributions in header distribution position, urgency levels for /// `urgency=` metadata values. pub fn get_completions( parse: &debian_changelog::Parse, source_text: &str, position: Position, ) -> Vec { // Use syntax_node() + cast so completion keeps working on syntactically // invalid/incomplete input while the user is typing. let Some(changelog) = debian_changelog::ChangeLog::cast(parse.syntax_node()) else { return Vec::new(); }; match get_cursor_context(&changelog, source_text, position) { Some(CursorContext::DistributionValue { value_prefix }) => { get_distribution_completions(&value_prefix) } Some(CursorContext::UrgencyValue { value_prefix }) => { get_urgency_completions(&value_prefix) } None => Vec::new(), } } fn get_cursor_context( changelog: &debian_changelog::ChangeLog, source_text: &str, position: Position, ) -> Option { let offset = crate::position::try_position_to_offset(source_text, position)?; let entry = changelog.entry_at_offset(offset)?; let header = entry.header()?; if let Some(value_prefix) = distribution_prefix_at_offset(&header, offset) { return Some(CursorContext::DistributionValue { value_prefix }); } if let Some(value_prefix) = urgency_prefix_at_offset(&header, offset) { return Some(CursorContext::UrgencyValue { value_prefix }); } None } fn distribution_prefix_at_offset( header: &debian_changelog::EntryHeader, offset: TextSize, ) -> Option { let distributions = header .syntax() .children() .find(|n| n.kind() == debian_changelog::SyntaxKind::DISTRIBUTIONS)?; let range = distributions.text_range(); if !range_contains_offset(range, offset) { // If distributions are currently empty, still offer completions when the // cursor is between the version and semicolon (on whitespaces). if range.start() == range.end() && offset < range.start() { let version_end = header.syntax().children_with_tokens().find_map(|it| { let token = it.as_token()?; if token.kind() == debian_changelog::SyntaxKind::VERSION { Some(token.text_range().end()) } else { None } }); if version_end.is_some_and(|end| offset >= end) { return Some(String::new()); } } return None; } if range.start() == range.end() { return Some(String::new()); } let token = match distributions.token_at_offset(offset) { rowan::TokenAtOffset::Single(token) => Some(token), rowan::TokenAtOffset::Between(left, right) => { if left.kind() == debian_changelog::SyntaxKind::IDENTIFIER { Some(left) } else { Some(right) } } rowan::TokenAtOffset::None => None, }; if let Some(token) = token { if token.kind() == debian_changelog::SyntaxKind::IDENTIFIER { return Some(token_prefix(token.text(), token.text_range(), offset)); } } Some(String::new()) } fn urgency_prefix_at_offset( header: &debian_changelog::EntryHeader, offset: TextSize, ) -> Option { for metadata in header.metadata_nodes() { if !metadata .key() .is_some_and(|key| key.eq_ignore_ascii_case("urgency")) { continue; } let value_node = metadata .syntax() .children() .find(|n| n.kind() == debian_changelog::SyntaxKind::METADATA_VALUE); if let Some(value_node) = value_node { let value_range = value_node.text_range(); if !range_contains_offset(value_range, offset) { continue; } if value_range.start() == value_range.end() { return Some(String::new()); } let prefix_end = std::cmp::min(offset, value_range.end()); let token = match value_node.token_at_offset(prefix_end) { rowan::TokenAtOffset::Single(token) => Some(token), rowan::TokenAtOffset::Between(left, right) => { if left.kind() == debian_changelog::SyntaxKind::IDENTIFIER { Some(left) } else if right.kind() == debian_changelog::SyntaxKind::IDENTIFIER { Some(right) } else { None } } rowan::TokenAtOffset::None => None, }; if let Some(token) = token { if token.kind() == debian_changelog::SyntaxKind::IDENTIFIER { return Some(token_prefix(token.text(), token.text_range(), prefix_end)); } } return Some(String::new()); } // In incomplete input like `urgency=`, parser may produce no METADATA_VALUE. // Offer urgency completions when cursor is positioned right after '='. let equals = metadata .syntax() .children_with_tokens() .filter_map(|it| it.as_token().cloned()) .find(|token| token.kind() == debian_changelog::SyntaxKind::EQUALS); if let Some(equals) = equals { let metadata_range = metadata.syntax().text_range(); if offset >= equals.text_range().end() && offset <= metadata_range.end() { return Some(String::new()); } } else { continue; } } None } fn range_contains_offset(range: TextRange, offset: TextSize) -> bool { if range.start() == range.end() { offset == range.start() } else { offset >= range.start() && offset <= range.end() } } fn token_prefix(token_text: &str, token_range: TextRange, offset: TextSize) -> String { let relative_end: usize = (std::cmp::min(offset, token_range.end()) - token_range.start()).into(); let mut prefix_end = std::cmp::min(relative_end, token_text.len()); while !token_text.is_char_boundary(prefix_end) { prefix_end -= 1; } token_text[..prefix_end].to_string() } /// Get completion items for Debian distributions. pub fn get_distribution_completions(prefix: &str) -> Vec { let normalized_prefix = prefix.trim().to_ascii_lowercase(); get_debian_distributions() .into_iter() .filter(|dist| dist.to_ascii_lowercase().starts_with(&normalized_prefix)) .map(|dist| CompletionItem { label: dist.clone(), kind: Some(CompletionItemKind::VALUE), detail: Some("Debian distribution".to_string()), documentation: Some(Documentation::String(format!( "Target distribution: {}", dist ))), insert_text: Some(dist), ..Default::default() }) .collect() } /// Get completion items for urgency levels. pub fn get_urgency_completions(prefix: &str) -> Vec { let normalized_prefix = prefix.trim().to_ascii_lowercase(); URGENCY_LEVELS .iter() .filter(|level| level.name.starts_with(&normalized_prefix)) .map(|level| CompletionItem { label: level.name.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some("Urgency level".to_string()), documentation: Some(Documentation::String(level.description.to_string())), insert_text: Some(level.name.to_string()), ..Default::default() }) .collect() } #[cfg(test)] mod tests { use super::*; fn parse_for(text: &str) -> debian_changelog::Parse { debian_changelog::ChangeLog::parse(text) } fn position_at(text: &str, byte_offset: usize) -> Position { crate::position::offset_to_position(text, TextSize::try_from(byte_offset).unwrap()) } #[test] fn test_get_completions_on_distribution_value() { let text = "foo (1.0-1) un; urgency=medium\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = parse_for(text); let offset = text.find("un;").unwrap() + 2; let completions = get_completions(&parsed, text, position_at(text, offset)); assert!(!completions.is_empty()); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"unstable")); assert!(labels.contains(&"UNRELEASED")); } #[test] fn test_get_completions_on_empty_distribution_slot_with_whitespace() { let text = "foo (1.0-1) ; urgency=medium\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = parse_for(text); let offset = text.find(" ;").unwrap() + 1; let completions = get_completions(&parsed, text, position_at(text, offset)); assert!(!completions.is_empty()); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"unstable")); } #[test] fn test_get_completions_on_urgency_value() { let text = "foo (1.0-1) unstable; urgency=me\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = parse_for(text); let offset = text.find("urgency=me").unwrap() + "urgency=me".len(); let completions = get_completions(&parsed, text, position_at(text, offset)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["medium"]); assert_eq!(completions[0].insert_text.as_deref(), Some("medium")); } #[test] fn test_get_completions_in_body_returns_empty() { let text = "foo (1.0-1) unstable; urgency=medium\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = parse_for(text); let offset = text.find("Initial").unwrap() + 2; let completions = get_completions(&parsed, text, position_at(text, offset)); assert!(completions.is_empty()); } #[test] fn test_get_completions_invalid_position_returns_empty() { let text = "foo (1.0-1) unstable; urgency=medium\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = parse_for(text); // Invalid character on an existing line. let completions = get_completions(&parsed, text, Position::new(0, 5000)); assert!(completions.is_empty()); // Invalid line beyond the end of file. let completions = get_completions(&parsed, text, Position::new(5000, 0)); assert!(completions.is_empty()); } #[test] fn test_get_completions_with_parse_errors_still_returns_contextual_results() { let text = "foo (1.0-1) unstable; urgency=\n"; let parsed = parse_for(text); assert!(!parsed.ok(), "test setup expects parse errors"); let offset = text.find("urgency=").unwrap() + "urgency=".len(); let completions = get_completions(&parsed, text, position_at(text, offset)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"medium")); // Invalid line beyond the end of file. let completions = get_completions(&parsed, text, Position::new(5000, 0)); assert!(completions.is_empty()); } #[test] fn test_distribution_completions_with_prefix() { let completions = get_distribution_completions("un"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"unstable")); assert!(labels.contains(&"UNRELEASED")); } #[test] fn test_urgency_completions_with_prefix() { let completions = get_urgency_completions("me"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["medium"]); } } debian-lsp-0.1.4/src/changelog/detection.rs000064400000000000000000000024431046102023000167110ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a Debian changelog file pub fn is_changelog_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/changelog") || path.ends_with("/debian/changelog") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_changelog_file() { let changelog_paths = vec![ "file:///path/to/debian/changelog", "file:///project/debian/changelog", "file:///changelog", "file:///some/path/changelog", ]; let non_changelog_paths = vec![ "file:///path/to/other.txt", "file:///path/to/changelog.txt", "file:///path/to/mychangelog", "file:///path/to/debian/changelog.backup", ]; for path in changelog_paths { let uri = path.parse::().unwrap(); assert!( is_changelog_file(&uri), "Should detect changelog file: {}", path ); } for path in non_changelog_paths { let uri = path.parse::().unwrap(); assert!( !is_changelog_file(&uri), "Should not detect as changelog file: {}", path ); } } } debian-lsp-0.1.4/src/changelog/fields.rs000064400000000000000000000060231046102023000161770ustar 00000000000000/// Debian changelog field definitions and common values use distro_info::{DebianDistroInfo, DistroInfo}; /// Debian urgency levels for changelog entries pub struct UrgencyLevel { pub name: &'static str, pub description: &'static str, } impl UrgencyLevel { pub const fn new(name: &'static str, description: &'static str) -> Self { Self { name, description } } } /// All available urgency levels pub const URGENCY_LEVELS: &[UrgencyLevel] = &[ UrgencyLevel::new("low", "Low urgency update"), UrgencyLevel::new("medium", "Medium urgency update"), UrgencyLevel::new("high", "High urgency update"), UrgencyLevel::new("critical", "Critical urgency update"), UrgencyLevel::new("emergency", "Emergency urgency update"), ]; /// Get Debian distribution names from distro-info-data /// Returns a vector of distribution names (codenames and aliases) pub fn get_debian_distributions() -> Vec { // Add common aliases first let mut distributions = vec![ "unstable".to_string(), "stable".to_string(), "testing".to_string(), "oldstable".to_string(), "experimental".to_string(), "sid".to_string(), "UNRELEASED".to_string(), ]; // Try to get distribution data from distro-info if let Ok(debian_info) = DebianDistroInfo::new() { // Add all release codenames for release in debian_info.iter() { let series = release.series(); if !distributions.contains(&series.to_string()) { distributions.push(series.to_string()); } } } distributions } #[cfg(test)] mod tests { use super::*; #[test] fn test_urgency_levels() { assert!(!URGENCY_LEVELS.is_empty()); assert_eq!(URGENCY_LEVELS.len(), 5); let urgency_names: Vec<_> = URGENCY_LEVELS.iter().map(|u| u.name).collect(); assert!(urgency_names.contains(&"low")); assert!(urgency_names.contains(&"medium")); assert!(urgency_names.contains(&"high")); assert!(urgency_names.contains(&"critical")); assert!(urgency_names.contains(&"emergency")); } #[test] fn test_urgency_level_validity() { for level in URGENCY_LEVELS { assert!(!level.name.is_empty()); assert!(!level.description.is_empty()); assert!( level.name.chars().all(|c| c.is_ascii_lowercase()), "Urgency level {} should be lowercase", level.name ); } } #[test] fn test_get_debian_distributions() { let distributions = get_debian_distributions(); assert!(!distributions.is_empty()); // Check that common aliases are present assert!(distributions.contains(&"unstable".to_string())); assert!(distributions.contains(&"stable".to_string())); assert!(distributions.contains(&"testing".to_string())); assert!(distributions.contains(&"UNRELEASED".to_string())); assert!(distributions.contains(&"sid".to_string())); } } debian-lsp-0.1.4/src/changelog/mod.rs000064400000000000000000000003301046102023000155030ustar 00000000000000pub mod actions; pub mod completion; pub mod detection; pub mod fields; pub mod semantic; pub use actions::*; pub use completion::*; pub use detection::is_changelog_file; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/changelog/semantic.rs000064400000000000000000000157361046102023000165470ustar 00000000000000//! Semantic token generation for Debian changelog files. use debian_changelog::SyntaxKind; use tower_lsp_server::ls_types::SemanticToken; use crate::deb822::semantic::{SemanticTokensBuilder, TokenType}; use crate::position::offset_to_position; /// Generate semantic tokens for a changelog file pub fn generate_semantic_tokens( parse: &debian_changelog::Parse, source_text: &str, ) -> Vec { let mut builder = SemanticTokensBuilder::new(); // Use syntax_node() to get tokens even with parse errors let syntax = parse.syntax_node(); for element in syntax.descendants_with_tokens() { if let rowan::NodeOrToken::Token(token) = element { let kind = token.kind(); let token_type = match kind { SyntaxKind::IDENTIFIER => { let parent_kind = token.parent().map(|p| p.kind()); match parent_kind { Some(SyntaxKind::ENTRY_HEADER) => Some(TokenType::ChangelogPackage), Some(SyntaxKind::METADATA_KEY) => Some(TokenType::ChangelogUrgency), Some(SyntaxKind::DISTRIBUTIONS) => Some(TokenType::ChangelogDistribution), Some(SyntaxKind::METADATA_VALUE) => Some(TokenType::ChangelogMetadataValue), _ => None, } } SyntaxKind::VERSION => Some(TokenType::ChangelogVersion), SyntaxKind::COMMENT => Some(TokenType::Comment), _ => { let parent_kind = token.parent().map(|p| p.kind()); match parent_kind { Some(SyntaxKind::METADATA_VALUE) => Some(TokenType::ChangelogMetadataValue), Some(SyntaxKind::TIMESTAMP) => Some(TokenType::ChangelogTimestamp), Some(SyntaxKind::MAINTAINER) => Some(TokenType::ChangelogMaintainer), Some(SyntaxKind::EMAIL) => Some(TokenType::ChangelogMaintainer), _ => None, } } }; if let Some(tt) = token_type { let range = token.text_range(); let start_pos = offset_to_position(source_text, range.start()); let length = (usize::from(range.end()) - usize::from(range.start())) as u32; if length > 0 { builder.push(start_pos.line, start_pos.character, length, tt, 0); } } } } builder.build() } #[cfg(test)] mod tests { use super::*; /// Helper to collect (token_type, length) pairs for easier assertions fn token_summary(tokens: &[SemanticToken]) -> Vec<(u32, u32)> { tokens.iter().map(|t| (t.token_type, t.length)).collect() } #[test] fn test_all_token_types_in_entry() { let text = "test-package (1.0-1) unstable; urgency=medium\n\n * Initial release.\n\n -- John Doe Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = debian_changelog::ChangeLog::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let summary = token_summary(&tokens); // Verify we see all expected token types let types: Vec = summary.iter().map(|(tt, _)| *tt).collect(); assert!( types.contains(&(TokenType::ChangelogPackage as u32)), "Missing ChangelogPackage in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogVersion as u32)), "Missing ChangelogVersion in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogDistribution as u32)), "Missing ChangelogDistribution in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogUrgency as u32)), "Missing ChangelogUrgency in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogMetadataValue as u32)), "Missing Value (metadata value) in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogMaintainer as u32)), "Missing ChangelogMaintainer in {types:?}" ); assert!( types.contains(&(TokenType::ChangelogTimestamp as u32)), "Missing ChangelogTimestamp in {types:?}" ); // First token should be the package name assert_eq!(tokens[0].delta_line, 0); assert_eq!(tokens[0].delta_start, 0); assert_eq!(tokens[0].token_type, TokenType::ChangelogPackage as u32); assert_eq!(tokens[0].length, 12); } #[test] fn test_maintainer_and_timestamp() { let text = "pkg (1.0-1) unstable; urgency=low\n\n * Change.\n\n -- Test User Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = debian_changelog::ChangeLog::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let summary = token_summary(&tokens); let has_maintainer = summary .iter() .any(|(tt, _)| *tt == TokenType::ChangelogMaintainer as u32); assert!(has_maintainer, "Should have a maintainer token"); let has_timestamp = summary .iter() .any(|(tt, _)| *tt == TokenType::ChangelogTimestamp as u32); assert!(has_timestamp, "Should have a timestamp token"); } #[test] fn test_multiple_entries() { let text = "\ pkg (2.0-1) unstable; urgency=medium * Second release. -- A Mon, 01 Jan 2025 12:00:00 +0000 pkg (1.0-1) unstable; urgency=low * First release. -- B Mon, 01 Jan 2024 12:00:00 +0000 "; let parsed = debian_changelog::ChangeLog::parse(text); let tokens = generate_semantic_tokens(&parsed, text); // Should have package tokens for both entries let package_tokens: Vec<_> = tokens .iter() .filter(|t| t.token_type == TokenType::ChangelogPackage as u32) .collect(); assert_eq!(package_tokens.len(), 2, "Should have 2 package name tokens"); // Should have version tokens for both entries let version_tokens: Vec<_> = tokens .iter() .filter(|t| t.token_type == TokenType::ChangelogVersion as u32) .collect(); assert_eq!(version_tokens.len(), 2, "Should have 2 version tokens"); } #[test] fn test_multiple_distributions() { let text = "pkg (1.0-1) unstable testing; urgency=low\n\n * Change.\n\n -- T Mon, 01 Jan 2024 12:00:00 +0000\n"; let parsed = debian_changelog::ChangeLog::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let dist_tokens: Vec<_> = tokens .iter() .filter(|t| t.token_type == TokenType::ChangelogDistribution as u32) .collect(); assert_eq!( dist_tokens.len(), 2, "Should have 2 distribution tokens for 'unstable testing'" ); } } debian-lsp-0.1.4/src/control/actions.rs000064400000000000000000000171671046102023000161350ustar 00000000000000use crate::position::text_range_to_lsp_range; use crate::workspace::FieldCasingIssue; use text_size::TextRange; use tower_lsp_server::ls_types::*; /// Generate a wrap-and-sort code action for a control file /// /// This function creates a code action that wraps and sorts fields in paragraphs /// that overlap with the requested text range. /// /// # Arguments /// * `uri` - The URI of the control file /// * `source_text` - The source text of the file /// * `parsed` - The parsed control file /// * `text_range` - The text range to operate on /// /// # Returns /// A code action if applicable paragraphs are found, None otherwise pub fn get_wrap_and_sort_action( uri: &Uri, source_text: &str, parsed: &debian_control::lossless::Parse, text_range: TextRange, ) -> Option { let control = parsed.clone().to_result().ok()?; let mut edits = Vec::new(); // Check if source paragraph is in range if let Some(source) = control.source_in_range(text_range) { let para_range = source.as_deb822().text_range(); let mut source = source.clone(); source.wrap_and_sort(deb822_lossless::Indentation::Spaces(1), false, Some(79)); let lsp_range = text_range_to_lsp_range(source_text, para_range); edits.push(TextEdit { range: lsp_range, new_text: source.to_string(), }); } // Check each binary paragraph in range for binary in control.binaries_in_range(text_range) { let para_range = binary.as_deb822().text_range(); let mut binary = binary.clone(); binary.wrap_and_sort(deb822_lossless::Indentation::Spaces(1), false, Some(79)); let lsp_range = text_range_to_lsp_range(source_text, para_range); edits.push(TextEdit { range: lsp_range, new_text: binary.to_string(), }); } if edits.is_empty() { return None; } let workspace_edit = WorkspaceEdit { changes: Some(vec![(uri.clone(), edits)].into_iter().collect()), ..Default::default() }; let action = CodeAction { title: "Wrap and sort".to_string(), kind: Some(CodeActionKind::SOURCE_ORGANIZE_IMPORTS), edit: Some(workspace_edit), ..Default::default() }; Some(CodeActionOrCommand::CodeAction(action)) } /// Generate field casing fix actions for a control file /// /// # Arguments /// * `uri` - The URI of the control file /// * `source_text` - The source text of the file /// * `issues` - The field casing issues found /// * `diagnostics` - The diagnostics from the context /// /// # Returns /// A vector of code actions for fixing field casing pub fn get_field_casing_actions( uri: &Uri, source_text: &str, issues: Vec, diagnostics: &[Diagnostic], ) -> Vec { let mut actions = Vec::new(); for issue in issues { let lsp_range = text_range_to_lsp_range(source_text, issue.field_range); // Check if there's a matching diagnostic in the context let matching_diagnostics = diagnostics .iter() .filter(|d| { d.range == lsp_range && d.code == Some(NumberOrString::String("field-casing".to_string())) }) .cloned() .collect::>(); // Create a code action to fix the casing let edit = TextEdit { range: lsp_range, new_text: issue.standard_name.clone(), }; let workspace_edit = WorkspaceEdit { changes: Some(vec![(uri.clone(), vec![edit])].into_iter().collect()), ..Default::default() }; let action = CodeAction { title: format!( "Fix field casing: {} -> {}", issue.field_name, issue.standard_name ), kind: Some(CodeActionKind::QUICKFIX), edit: Some(workspace_edit), diagnostics: if !matching_diagnostics.is_empty() { Some(matching_diagnostics) } else { None }, ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(action)); } actions } #[cfg(test)] mod tests { use super::*; #[test] fn test_wrap_and_sort_action() { let input = r#"Source: test-package Maintainer: Test User Build-Depends: debhelper-compat (= 13), foo, bar, baz Package: test-package Architecture: any Depends: libc6, libfoo, libbar Description: A test package This is a test package. "#; let parsed = debian_control::lossless::Control::parse(input); let uri: Uri = "file:///debian/control".parse().unwrap(); let text_range = TextRange::new(0.into(), (input.len() as u32).into()); let action = get_wrap_and_sort_action(&uri, input, &parsed, text_range); // Should return a code action assert!(action.is_some()); let CodeActionOrCommand::CodeAction(action) = action.unwrap() else { panic!("Expected CodeAction"); }; assert_eq!(action.title, "Wrap and sort"); assert_eq!(action.kind, Some(CodeActionKind::SOURCE_ORGANIZE_IMPORTS)); // Extract the edits let workspace_edit = action.edit.expect("Should have an edit"); let changes = workspace_edit.changes.expect("Should have changes"); let edits = changes.get(&uri).expect("Should have edits for the URI"); // Should have edits for both source and binary paragraphs assert_eq!(edits.len(), 2); // Get the formatted source paragraph let formatted_source = &edits[0].new_text; // Get the formatted binary paragraph let formatted_binary = &edits[1].new_text; // Print the actual output for debugging println!("Formatted source:\n{}", formatted_source); println!("Formatted binary:\n{}", formatted_binary); // Verify the exact formatted output for source paragraph let expected_source = "Source: test-package\nMaintainer: Test User \nBuild-Depends:bar, baz, debhelper-compat (= 13), foo\n"; assert_eq!(formatted_source, expected_source); // Verify the exact formatted output for binary paragraph let expected_binary = "Package: test-package\nArchitecture: any\nDepends:libbar, libc6, libfoo\nDescription: A test package\n This is a test package.\n"; assert_eq!(formatted_binary, expected_binary); } #[test] fn test_field_casing_actions() { let input = r#"source: test-package maintainer: Test User "#; let uri: Uri = "file:///debian/control".parse().unwrap(); let issues = vec![ FieldCasingIssue { field_name: "source".to_string(), standard_name: "Source".to_string(), field_range: TextRange::new(0.into(), 6.into()), }, FieldCasingIssue { field_name: "maintainer".to_string(), standard_name: "Maintainer".to_string(), field_range: TextRange::new(21.into(), 31.into()), }, ]; let actions = get_field_casing_actions(&uri, input, issues, &[]); assert_eq!(actions.len(), 2); let CodeActionOrCommand::CodeAction(ref action) = actions[0] else { panic!("Expected CodeAction"); }; assert_eq!(action.title, "Fix field casing: source -> Source"); let CodeActionOrCommand::CodeAction(ref action) = actions[1] else { panic!("Expected CodeAction"); }; assert_eq!(action.title, "Fix field casing: maintainer -> Maintainer"); } } debian-lsp-0.1.4/src/control/completion.rs000064400000000000000000000331311046102023000166330ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind}; use super::fields::{ CONTROL_FIELDS, CONTROL_PRIORITY_VALUES, CONTROL_SECTION_AREAS, CONTROL_SECTION_VALUES, CONTROL_SPECIAL_SECTION_VALUES, }; use super::relation_completion; use crate::architecture::SharedArchitectureList; use crate::package_cache::SharedPackageCache; /// Get completions for a control file at the given cursor position. /// /// Uses the parsed deb822 document for position-aware completions: /// if on a field value, returns value completions; otherwise returns /// field name completions. Relationship field completions are not /// included here because they require async access to the package cache; /// use [`get_async_field_value_completions`] for those. pub fn get_completions( deb822: &deb822_lossless::Deb822, source_text: &str, position: tower_lsp_server::ls_types::Position, ) -> Vec { crate::deb822::completion::get_completions( deb822, source_text, position, CONTROL_FIELDS, get_field_value_completions, ) } /// Get value completions for specific control file fields (sync only). /// /// Returns completions for Section and Priority fields. /// Returns empty for relationship fields (handled async separately) /// and for unknown fields. pub fn get_field_value_completions(field_name: &str, prefix: &str) -> Vec { if field_name.eq_ignore_ascii_case("Section") { get_section_value_completions(prefix) } else if field_name.eq_ignore_ascii_case("Priority") { get_priority_value_completions(prefix) } else { vec![] } } /// Get async value completions for control file fields that need the package cache. /// /// Returns `Some` with completions for relationship fields, `None` for other fields. pub async fn get_async_field_value_completions( field_name: &str, prefix: &str, package_cache: &SharedPackageCache, architecture_list: &SharedArchitectureList, ) -> Option> { if relation_completion::is_relationship_field(field_name) { Some( relation_completion::get_relationship_completions( prefix, package_cache, architecture_list, ) .await, ) } else { None } } /// Get completion items for Debian priority values. pub fn get_priority_value_completions(prefix: &str) -> Vec { let normalized_prefix = prefix.trim().to_ascii_lowercase(); CONTROL_PRIORITY_VALUES .iter() .filter(|(value, _)| value.starts_with(&normalized_prefix)) .map(|&(value, description)| CompletionItem { label: value.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some(description.to_string()), insert_text: Some(value.to_string()), ..Default::default() }) .collect() } /// Get completion items for Debian section values. /// /// Includes both `section` and `area/section` forms. pub fn get_section_value_completions(prefix: &str) -> Vec { let normalized_prefix = prefix.trim().to_ascii_lowercase(); let mut completions = Vec::new(); for &(section, description) in CONTROL_SECTION_VALUES { if section.starts_with(&normalized_prefix) { completions.push(CompletionItem { label: section.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some(description.to_string()), insert_text: Some(section.to_string()), ..Default::default() }); } } for &area in CONTROL_SECTION_AREAS { for &(section, description) in CONTROL_SECTION_VALUES { let qualified = format!("{}/{}", area, section); if qualified.starts_with(&normalized_prefix) { completions.push(CompletionItem { label: qualified.clone(), kind: Some(CompletionItemKind::VALUE), detail: Some(description.to_string()), insert_text: Some(qualified), ..Default::default() }); } } } for &(special, description) in CONTROL_SPECIAL_SECTION_VALUES { if special.starts_with(&normalized_prefix) { completions.push(CompletionItem { label: special.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some(description.to_string()), insert_text: Some(special.to_string()), ..Default::default() }); } } completions } #[cfg(test)] mod tests { use super::*; use crate::architecture::SharedArchitectureList; use crate::package_cache::TestPackageCache; use std::sync::Arc; use tower_lsp_server::ls_types::Position; fn test_cache() -> SharedPackageCache { TestPackageCache::new_shared(&[ ("cmake", Some("cross-platform make")), ("debhelper-compat", None), ( "dh-python", Some("Debian helper tools for packaging Python"), ), ("libssl-dev", None), ("pkg-config", None), ]) } fn test_arch_list() -> SharedArchitectureList { Arc::new(tokio::sync::RwLock::new(vec![ "amd64".to_string(), "arm64".to_string(), "armhf".to_string(), "i386".to_string(), ])) } #[test] fn test_get_completions_on_field_key() { let text = "Source: test\nSection: py\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(1, 3)); // Should have field completions only assert!(completions .iter() .all(|c| c.kind == Some(CompletionItemKind::FIELD))); } #[test] fn test_get_completions_on_section_value() { let text = "Source: test\nSection: py\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(1, 11)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"python")); } #[test] fn test_get_completions_on_priority_value() { let text = "Source: test\nPriority: op\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(1, 12)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["optional"]); } #[test] fn test_priority_value_completions() { let completions = get_priority_value_completions(""); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"required")); assert!(labels.contains(&"important")); assert!(labels.contains(&"standard")); assert!(labels.contains(&"optional")); assert!(labels.contains(&"extra")); } #[test] fn test_priority_value_completions_with_prefix() { let completions = get_priority_value_completions("op"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["optional"]); } #[test] fn test_priority_value_completions_with_uppercase_prefix() { let completions = get_priority_value_completions("OP"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["optional"]); } #[test] fn test_section_value_completions() { let completions = get_section_value_completions(""); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"admin")); assert!(labels.contains(&"python")); assert!(labels.contains(&"debian-installer")); assert!(labels.contains(&"non-free/python")); assert!(!labels.contains(&"non-free/debian-installer")); // Check that descriptions are present let admin = completions.iter().find(|c| c.label == "admin").unwrap(); assert_eq!( admin.detail.as_deref(), Some("System administration utilities") ); } #[test] fn test_section_value_completions_with_area_prefix() { let completions = get_section_value_completions("non-free/"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"non-free/python")); assert!(!labels.contains(&"python")); assert!(!labels.contains(&"non-free/debian-installer")); // Area-qualified sections use the same description as the base section let nf_python = completions .iter() .find(|c| c.label == "non-free/python") .unwrap(); assert_eq!( nf_python.detail.as_deref(), Some("Python programming language") ); } #[test] fn test_get_field_value_completions_for_section() { let completions = get_field_value_completions("Section", "py"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"python")); } #[test] fn test_get_field_value_completions_for_priority() { let completions = get_field_value_completions("Priority", "op"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["optional"]); } #[test] fn test_get_field_value_completions_for_unknown_field() { let completions = get_field_value_completions("Homepage", "http"); assert!(completions.is_empty()); } #[tokio::test] async fn test_async_field_value_completions_for_depends() { let cache = test_cache(); let completions = get_async_field_value_completions("Depends", "cm", &cache, &test_arch_list()) .await .expect("Should return completions"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["cmake"]); } #[tokio::test] async fn test_async_field_value_completions_for_build_depends() { let cache = test_cache(); let completions = get_async_field_value_completions("Build-Depends", "", &cache, &test_arch_list()) .await .expect("Should return completions"); assert!(!completions.is_empty()); } #[tokio::test] async fn test_async_field_value_completions_for_non_relationship() { let cache = test_cache(); let completions = get_async_field_value_completions("Homepage", "http", &cache, &test_arch_list()).await; assert!(completions.is_none()); } /// End-to-end test: get_cursor_context → get_async_field_value_completions /// for a single-line Build-Depends field with cursor after ": ". #[tokio::test] async fn test_end_to_end_build_depends_empty_value() { let text = "Build-Depends: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = crate::deb822::completion::get_cursor_context( &deb822, text, tower_lsp_server::ls_types::Position::new(0, 15), ) .expect("Should have context"); match ctx { crate::deb822::completion::CursorContext::FieldValue { field_name, value_prefix, } => { assert_eq!(field_name, "Build-Depends"); assert_eq!(value_prefix, ""); let cache = test_cache(); let completions = get_async_field_value_completions( &field_name, &value_prefix, &cache, &test_arch_list(), ) .await .expect("Should return completions for relationship field"); assert!(!completions.is_empty(), "Should have package completions"); } other => panic!("Expected FieldValue, got {:?}", other), } } /// End-to-end test: cursor in middle of Build-Depends value. #[tokio::test] async fn test_end_to_end_build_depends_partial_name() { let text = "Build-Depends: dh\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = crate::deb822::completion::get_cursor_context( &deb822, text, tower_lsp_server::ls_types::Position::new(0, 17), ) .expect("Should have context"); match ctx { crate::deb822::completion::CursorContext::FieldValue { field_name, value_prefix, } => { assert_eq!(field_name, "Build-Depends"); assert_eq!(value_prefix, "dh"); let cache = test_cache(); let completions = get_async_field_value_completions( &field_name, &value_prefix, &cache, &test_arch_list(), ) .await .expect("Should return completions for relationship field"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["dh-python"]); } other => panic!("Expected FieldValue, got {:?}", other), } } } debian-lsp-0.1.4/src/control/detection.rs000064400000000000000000000023731046102023000164440ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a Debian control file pub fn is_control_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/control") || path.ends_with("/debian/control") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_control_file() { let control_paths = vec![ "file:///path/to/debian/control", "file:///project/debian/control", "file:///control", "file:///some/path/control", ]; let non_control_paths = vec![ "file:///path/to/other.txt", "file:///path/to/control.txt", "file:///path/to/mycontrol", "file:///path/to/debian/control.backup", ]; for path in control_paths { let uri = path.parse::().unwrap(); assert!( is_control_file(&uri), "Should detect control file: {}", path ); } for path in non_control_paths { let uri = path.parse::().unwrap(); assert!( !is_control_file(&uri), "Should not detect as control file: {}", path ); } } } debian-lsp-0.1.4/src/control/diagnostics.rs000064400000000000000000000144521046102023000167760ustar 00000000000000use text_size::TextRange; use tower_lsp_server::ls_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Position, Range}; use crate::workspace::FieldCasingIssue; /// All types of diagnostic issues that can be found in a control file #[derive(Debug, Clone)] pub enum DiagnosticIssue { FieldCasing(FieldCasingIssue), ParseError { message: String, range: Option, }, } /// Find all diagnostic issues in a control file, optionally within a specific range pub fn find_all_issues( parsed: &debian_control::lossless::Parse, range: Option, ) -> Vec { let mut issues = Vec::new(); // Add parse errors with position information for error in parsed.positioned_errors() { // If we have a range filter, check if this error is in range if let Some(filter_range) = range { if error.range.start() >= filter_range.end() || error.range.end() <= filter_range.start() { continue; // Skip errors outside the range } } issues.push(DiagnosticIssue::ParseError { message: error.message.to_string(), range: Some(error.range), }); } // Add field casing issues if let Ok(control) = parsed.clone().to_result() { for paragraph in control.as_deb822().paragraphs() { for entry in paragraph.entries() { let entry_range = entry.text_range(); // If a range is specified, check if this entry is within it if let Some(filter_range) = range { if entry_range.start() >= filter_range.end() || entry_range.end() <= filter_range.start() { continue; // Skip entries outside the range } } if let Some(field_name) = entry.key() { if let Some(standard_name) = crate::control::get_standard_field_name(&field_name) { if field_name != standard_name { let field_range = TextRange::new( entry_range.start(), entry_range.start() + text_size::TextSize::of(field_name.as_str()), ); issues.push(DiagnosticIssue::FieldCasing(FieldCasingIssue { field_name, standard_name: standard_name.to_string(), field_range, })); } } } } } } issues } /// Convert a DiagnosticIssue to an LSP Diagnostic pub fn issue_to_diagnostic(issue: DiagnosticIssue, source_text: &str) -> Diagnostic { match issue { DiagnosticIssue::ParseError { message, range } => { let lsp_range = if let Some(range) = range { crate::position::text_range_to_lsp_range(source_text, range) } else { // Fallback to (0,0) if no range is available Range { start: Position { line: 0, character: 0, }, end: Position { line: 0, character: 0, }, } }; Diagnostic { range: lsp_range, severity: Some(DiagnosticSeverity::ERROR), message, ..Default::default() } } DiagnosticIssue::FieldCasing(casing) => { let lsp_range = crate::position::text_range_to_lsp_range(source_text, casing.field_range); Diagnostic { range: lsp_range, severity: Some(DiagnosticSeverity::WARNING), code: Some(NumberOrString::String("field-casing".to_string())), source: Some("debian-lsp".to_string()), message: format!( "Field name '{}' should be '{}'", casing.field_name, casing.standard_name ), ..Default::default() } } } } /// Find all field casing issues in a control file, optionally within a specific range pub fn find_field_casing_issues( parsed: &debian_control::lossless::Parse, range: Option, ) -> Vec { find_all_issues(parsed, range) .into_iter() .filter_map(|issue| match issue { DiagnosticIssue::FieldCasing(casing) => Some(casing), _ => None, }) .collect() } /// Get all LSP diagnostics for a control file pub fn get_diagnostics( source_text: &str, parsed: &debian_control::lossless::Parse, ) -> Vec { find_all_issues(parsed, None) .into_iter() .map(|issue| issue_to_diagnostic(issue, source_text)) .collect() } #[cfg(test)] mod tests { use super::*; use crate::workspace::Workspace; #[test] fn test_find_all_issues_correct_casing() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/control").unwrap(); let content = "Source: test-package\nMaintainer: Test \n\nPackage: test-package\nArchitecture: amd64\nDescription: A test package\n"; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_control(file); let issues = find_all_issues(&parsed, None); assert!(issues.is_empty()); } #[test] fn test_find_all_issues_incorrect_casing() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/control").unwrap(); let content = "source: test-package\nmaintainer: Test \n\npackage: test-package\narchitecture: amd64\ndescription: A test package\n"; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_control(file); let issues = find_all_issues(&parsed, None); assert!(!issues.is_empty()); } } debian-lsp-0.1.4/src/control/fields.rs000064400000000000000000000174721046102023000157420ustar 00000000000000use crate::deb822::completion::FieldInfo; /// All available Debian control file fields pub const CONTROL_FIELDS: &[FieldInfo] = &[ FieldInfo::new("Source", "Name of the source package"), FieldInfo::new("Section", "Classification of the package"), FieldInfo::new("Priority", "Priority of the package"), FieldInfo::new("Maintainer", "Package maintainer's name and email"), FieldInfo::new("Uploaders", "Additional maintainers"), FieldInfo::new("Build-Depends", "Build dependencies"), FieldInfo::new( "Build-Depends-Indep", "Architecture-independent build dependencies", ), FieldInfo::new("Build-Conflicts", "Packages that conflict during build"), FieldInfo::new("Standards-Version", "Debian Policy version"), FieldInfo::new("Homepage", "Upstream project homepage"), FieldInfo::new("Vcs-Browser", "Web interface for VCS"), FieldInfo::new("Vcs-Git", "Git repository URL"), FieldInfo::new("Package", "Binary package name"), FieldInfo::new("Architecture", "Supported architectures"), FieldInfo::new("Multi-Arch", "Multi-architecture support"), FieldInfo::new("Depends", "Package dependencies"), FieldInfo::new("Pre-Depends", "Pre-installation dependencies"), FieldInfo::new("Recommends", "Recommended packages"), FieldInfo::new("Suggests", "Suggested packages"), FieldInfo::new("Enhances", "Packages enhanced by this one"), FieldInfo::new("Conflicts", "Conflicting packages"), FieldInfo::new("Breaks", "Packages broken by this one"), FieldInfo::new("Provides", "Virtual packages provided"), FieldInfo::new("Replaces", "Packages replaced by this one"), FieldInfo::new("Description", "Package description"), FieldInfo::new("Essential", "Essential package flag"), FieldInfo::new("Rules-Requires-Root", "Root privileges requirement"), ]; /// Get the standard casing for a field name pub fn get_standard_field_name(field_name: &str) -> Option<&'static str> { crate::deb822::completion::get_standard_field_name(CONTROL_FIELDS, field_name) } /// Debian policy-recognized priority values for control files. /// Each entry is (value, description). pub const CONTROL_PRIORITY_VALUES: &[(&str, &str)] = &[ ("required", "Essential for the system to function"), ( "important", "Important programs, including those expected on a Unix-like system", ), ( "standard", "Reasonably small but not too limited character-mode system", ), ( "optional", "All packages not required for a reasonably functional system", ), ("extra", "Deprecated alias for optional"), ]; /// Debian policy section values for normal packages. /// Each entry is (value, description). // TODO: Read section list from an external file or Debian policy data instead of hardcoding. pub const CONTROL_SECTION_VALUES: &[(&str, &str)] = &[ ("admin", "System administration utilities"), ("cli-mono", "Mono/CLI based programs"), ("comm", "Communication programs"), ("database", "Database servers and tools"), ("debug", "Debug packages"), ("devel", "Development tools and libraries"), ("doc", "Documentation"), ("editors", "Text editors"), ("education", "Educational software"), ("electronics", "Electronics and electrical engineering"), ("embedded", "Embedded systems software"), ("fonts", "Font packages"), ("games", "Games and amusements"), ("gnome", "GNOME desktop environment"), ("gnu-r", "GNU R statistical system"), ("gnustep", "GNUstep environment"), ("graphics", "Graphics tools"), ("hamradio", "Ham radio software"), ("haskell", "Haskell programming language"), ("httpd", "Web servers"), ("interpreters", "Interpreted languages"), ("introspection", "GObject introspection data"), ("java", "Java programming language"), ("javascript", "JavaScript programming"), ("kde", "KDE desktop environment"), ("kernel", "Kernel and kernel modules"), ("libdevel", "Development libraries"), ("libs", "Shared libraries"), ("lisp", "Lisp programming language"), ("localization", "Localization and internationalization"), ("mail", "Email programs"), ("math", "Mathematics and numerical computation"), ("metapackages", "Metapackages"), ("misc", "Miscellaneous"), ("net", "Networking tools"), ("news", "Usenet news"), ("ocaml", "OCaml programming language"), ("oldlibs", "Obsolete libraries"), ("otherosfs", "Other OS file systems"), ("perl", "Perl programming language"), ("php", "PHP programming language"), ("python", "Python programming language"), ("ruby", "Ruby programming language"), ("rust", "Rust programming language"), ("science", "Scientific software"), ("shells", "Command-line shells"), ("sound", "Sound and audio"), ("tasks", "Task packages for installation"), ("tex", "TeX typesetting system"), ("text", "Text processing utilities"), ("utils", "General-purpose utilities"), ("vcs", "Version control systems"), ("video", "Video tools"), ("web", "Web browsers and tools"), ("x11", "X Window System"), ("xfce", "Xfce desktop environment"), ("zope", "Zope/Plone framework"), ]; /// Debian archive areas used as section prefixes in control fields. /// /// Section field values can be `area/section` for non-main archive areas. pub const CONTROL_SECTION_AREAS: &[&str] = &["contrib", "non-free", "non-free-firmware"]; /// Debian policy special section values. /// /// `debian-installer` is used for installer packages and not normal packages. pub const CONTROL_SPECIAL_SECTION_VALUES: &[(&str, &str)] = &[("debian-installer", "Debian installer components")]; #[cfg(test)] mod tests { use super::*; #[test] fn test_control_fields() { assert!(!CONTROL_FIELDS.is_empty()); assert!(CONTROL_FIELDS.len() >= 20); // Test specific fields exist let field_names: Vec<_> = CONTROL_FIELDS.iter().map(|f| f.name).collect(); assert!(field_names.contains(&"Source")); assert!(field_names.contains(&"Package")); assert!(field_names.contains(&"Depends")); assert!(field_names.contains(&"Build-Depends")); } #[test] fn test_control_field_validity() { for field in CONTROL_FIELDS { assert!(!field.name.is_empty()); assert!(!field.description.is_empty()); assert!( field .name .chars() .all(|c| c.is_ascii_alphanumeric() || c == '-'), "Field {} contains invalid characters", field.name ); } } #[test] fn test_control_priority_values() { let names: Vec<_> = CONTROL_PRIORITY_VALUES.iter().map(|(n, _)| *n).collect(); assert_eq!( names, &["required", "important", "standard", "optional", "extra"] ); for (_, desc) in CONTROL_PRIORITY_VALUES { assert!(!desc.is_empty()); } } #[test] fn test_control_section_values() { let names: Vec<_> = CONTROL_SECTION_VALUES.iter().map(|(n, _)| *n).collect(); assert!(!names.is_empty()); assert!(names.contains(&"admin")); assert!(names.contains(&"python")); assert!(names.contains(&"xfce")); assert!(!names.contains(&"debian-installer")); for (_, desc) in CONTROL_SECTION_VALUES { assert!(!desc.is_empty()); } } #[test] fn test_control_section_areas() { assert_eq!( CONTROL_SECTION_AREAS, &["contrib", "non-free", "non-free-firmware"] ); } #[test] fn test_control_special_section_values() { let names: Vec<_> = CONTROL_SPECIAL_SECTION_VALUES .iter() .map(|(n, _)| *n) .collect(); assert_eq!(names, &["debian-installer"]); } } debian-lsp-0.1.4/src/control/mod.rs000064400000000000000000000004551046102023000152440ustar 00000000000000pub mod actions; pub mod completion; pub mod detection; pub mod diagnostics; pub mod fields; mod relation_completion; pub mod semantic; pub use actions::*; pub use completion::*; pub use detection::is_control_file; pub use fields::get_standard_field_name; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/control/relation_completion.rs000064400000000000000000001070451046102023000205360ustar 00000000000000use debian_control::lossless::relations::Relations; use debian_control::relations::SyntaxKind as RelSyntaxKind; use rowan::NodeOrToken; use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Documentation}; use crate::architecture::SharedArchitectureList; use crate::package_cache::SharedPackageCache; /// Relationship field names in debian/control. const RELATIONSHIP_FIELDS: &[&str] = &[ "Depends", "Pre-Depends", "Recommends", "Suggests", "Enhances", "Conflicts", "Breaks", "Provides", "Replaces", "Build-Depends", "Build-Depends-Indep", "Build-Depends-Arch", "Build-Conflicts", "Build-Conflicts-Indep", "Build-Conflicts-Arch", ]; /// Returns true if the field name is a relationship field. pub(crate) fn is_relationship_field(field_name: &str) -> bool { RELATIONSHIP_FIELDS .iter() .any(|f| f.eq_ignore_ascii_case(field_name)) } /// Version constraint operators in Debian relationships. const VERSION_OPERATORS: &[(&str, &str)] = &[ (">=", "Greater than or equal"), ("<=", "Less than or equal"), ("=", "Exactly equal"), (">>", "Strictly greater than"), ("<<", "Strictly less than"), ]; /// Where the cursor is within a relationship field value. #[derive(Debug, Clone, PartialEq, Eq)] enum RelationCompletionPosition { /// At a package name position (start, after comma, or after pipe). PackageName(String), /// After `(` — expecting a version operator. VersionOperator(String), /// After a version operator — expecting a version string. Version(String), /// Inside `[` — expecting an architecture name. Architecture(String), /// After `:` on a package name — expecting an architecture qualifier. ArchQualifier(String), /// Inside `<` — expecting a build profile name. BuildProfile(String), /// Inside `${` — expecting a substvar name. Substvar(String), } /// Determine the completion position within a relationship field value prefix. /// /// Parses the prefix using the lossless relations parser and walks the /// concrete syntax tree to determine context at the end of the input. fn determine_relation_position(prefix: &str) -> RelationCompletionPosition { let (relations, _errors) = Relations::parse_relaxed(prefix, true); let syntax = relations.syntax(); // Find the last non-whitespace token in the tree. let last_token = last_significant_token(syntax); let Some(token) = last_token else { return RelationCompletionPosition::PackageName(String::new()); }; // Walk up to find what node context we're in. let parent_kind = token .parent() .map(|p| p.kind()) .unwrap_or(RelSyntaxKind::ROOT); // Check if we're inside a VERSION node (i.e. inside parentheses of a version constraint). let in_version = token .parent_ancestors() .any(|n| n.kind() == RelSyntaxKind::VERSION); if in_version { // Inside a version constraint like "libc6 (>= 2.1" or "libc6 (>" or "libc6 (" let version_node = token .parent_ancestors() .find(|n| n.kind() == RelSyntaxKind::VERSION) .unwrap(); // Check if we have a CONSTRAINT child node with any operator tokens. let constraint_text: String = version_node .children() .find(|n| n.kind() == RelSyntaxKind::CONSTRAINT) .map(|c| c.text().to_string()) .unwrap_or_default(); if constraint_text.is_empty() { // After "(" with no operator yet return RelationCompletionPosition::VersionOperator(String::new()); } // Is the last significant token part of the constraint operator? // If so, check whether the operator is followed by whitespace in the // original input — if yes, the operator is complete and we're in // version position; otherwise we're still typing the operator. if parent_kind == RelSyntaxKind::CONSTRAINT { let has_trailing_ws = token.next_token().is_some_and(|t| { matches!(t.kind(), RelSyntaxKind::WHITESPACE | RelSyntaxKind::NEWLINE) }); if has_trailing_ws { // Operator is complete, now in version position return RelationCompletionPosition::Version(String::new()); } return RelationCompletionPosition::VersionOperator(constraint_text); } // We have a constraint; we're in version position. // Collect IDENT tokens after the constraint as the version prefix. let version_prefix: String = version_node .children_with_tokens() .filter_map(|it| match it { NodeOrToken::Token(t) if t.kind() == RelSyntaxKind::IDENT || t.kind() == RelSyntaxKind::COLON => { Some(t.text().to_string()) } _ => None, }) .collect(); return RelationCompletionPosition::Version(version_prefix); } // Check if we're inside a SUBSTVAR node (i.e. inside `${...}`). let in_substvar = token .parent_ancestors() .any(|n| n.kind() == RelSyntaxKind::SUBSTVAR); if in_substvar { let substvar_node = token .parent_ancestors() .find(|n| n.kind() == RelSyntaxKind::SUBSTVAR) .unwrap(); // Collect the text of IDENT and COLON tokens inside the substvar. let partial: String = substvar_node .children_with_tokens() .filter_map(|it| match it { NodeOrToken::Token(t) if t.kind() == RelSyntaxKind::IDENT || t.kind() == RelSyntaxKind::COLON => { Some(t.text().to_string()) } _ => None, }) .collect(); return RelationCompletionPosition::Substvar(partial); } // Check if we're inside an ARCHQUAL node (i.e. after ":" on a package name). let in_archqual = token .parent_ancestors() .any(|n| n.kind() == RelSyntaxKind::ARCHQUAL); if in_archqual { match token.kind() { RelSyntaxKind::COLON => { return RelationCompletionPosition::ArchQualifier(String::new()); } RelSyntaxKind::IDENT => { // If followed by whitespace, the qualifier is complete — // we're past the archqual, back to package name position. let has_trailing_ws = token.next_token().is_some_and(|t| { matches!(t.kind(), RelSyntaxKind::WHITESPACE | RelSyntaxKind::NEWLINE) }); if has_trailing_ws { return RelationCompletionPosition::PackageName(String::new()); } return RelationCompletionPosition::ArchQualifier(token.text().to_string()); } _ => { return RelationCompletionPosition::ArchQualifier(String::new()); } } } // Check if we're inside an ARCHITECTURES node (i.e. inside brackets). let in_architectures = token .parent_ancestors() .any(|n| n.kind() == RelSyntaxKind::ARCHITECTURES); if in_architectures { match token.kind() { RelSyntaxKind::L_BRACKET => { return RelationCompletionPosition::Architecture(String::new()); } RelSyntaxKind::IDENT => { // If followed by whitespace, the arch name is complete — // we're in position for a new architecture. let has_trailing_ws = token.next_token().is_some_and(|t| { matches!(t.kind(), RelSyntaxKind::WHITESPACE | RelSyntaxKind::NEWLINE) }); if has_trailing_ws { return RelationCompletionPosition::Architecture(String::new()); } // Check if preceded by "!" (negated arch). let negated = token .prev_token() .is_some_and(|t| t.kind() == RelSyntaxKind::NOT); let prefix = if negated { format!("!{}", token.text()) } else { token.text().to_string() }; return RelationCompletionPosition::Architecture(prefix); } RelSyntaxKind::NOT => { // "!" with no arch name yet return RelationCompletionPosition::Architecture("!".to_string()); } _ => { return RelationCompletionPosition::Architecture(String::new()); } } } // Check if we're inside a PROFILES node (i.e. inside angle brackets). let in_profiles = token .parent_ancestors() .any(|n| n.kind() == RelSyntaxKind::PROFILES); if in_profiles { match token.kind() { RelSyntaxKind::L_ANGLE => { return RelationCompletionPosition::BuildProfile(String::new()); } RelSyntaxKind::IDENT => { let has_trailing_ws = token.next_token().is_some_and(|t| { matches!(t.kind(), RelSyntaxKind::WHITESPACE | RelSyntaxKind::NEWLINE) }); if has_trailing_ws { return RelationCompletionPosition::BuildProfile(String::new()); } let negated = token .prev_token() .is_some_and(|t| t.kind() == RelSyntaxKind::NOT); let prefix = if negated { format!("!{}", token.text()) } else { token.text().to_string() }; return RelationCompletionPosition::BuildProfile(prefix); } RelSyntaxKind::NOT => { return RelationCompletionPosition::BuildProfile("!".to_string()); } _ => { return RelationCompletionPosition::BuildProfile(String::new()); } } } match token.kind() { RelSyntaxKind::COMMA | RelSyntaxKind::PIPE => { RelationCompletionPosition::PackageName(String::new()) } RelSyntaxKind::IDENT if parent_kind == RelSyntaxKind::RELATION => { // Could be a package name being typed RelationCompletionPosition::PackageName(token.text().to_string()) } _ => RelationCompletionPosition::PackageName(String::new()), } } /// Find the last non-whitespace token in a syntax tree. /// /// We walk forward from `first_token()` rather than using `last_token()` /// because rowan's `last_token()` returns `None` when the tree ends with /// an empty node (e.g. an ERROR node from incomplete input). fn last_significant_token( node: &rowan::SyntaxNode, ) -> Option> { let mut result = None; let mut tok = node.first_token(); while let Some(t) = tok { if !matches!(t.kind(), RelSyntaxKind::WHITESPACE | RelSyntaxKind::NEWLINE) { result = Some(t.clone()); } tok = t.next_token(); } result } /// Get completions for a relationship field value. pub(crate) async fn get_relationship_completions( prefix: &str, package_cache: &SharedPackageCache, architecture_list: &SharedArchitectureList, ) -> Vec { match determine_relation_position(prefix) { RelationCompletionPosition::PackageName(partial) => { let cache = package_cache.read().await; let packages = cache.get_packages_with_prefix(&partial); packages .iter() .map(|pkg| { let description = cache.get_description(pkg).map(|s| s.to_string()); let documentation = description .as_deref() .map(|d| Documentation::String(d.to_string())); CompletionItem { label: pkg.clone(), kind: Some(CompletionItemKind::VALUE), detail: description.or_else(|| Some("Package".to_string())), documentation, ..Default::default() } }) .collect() } RelationCompletionPosition::VersionOperator(partial) => VERSION_OPERATORS .iter() .filter(|(op, _)| op.starts_with(&partial)) .map(|&(op, desc)| CompletionItem { label: op.to_string(), kind: Some(CompletionItemKind::OPERATOR), detail: Some(desc.to_string()), insert_text: Some(format!("{} ", op)), ..Default::default() }) .collect(), RelationCompletionPosition::Version(partial) => { get_version_completions(prefix, &partial, package_cache).await } RelationCompletionPosition::Architecture(partial) => { get_architecture_completions(&partial, architecture_list).await } RelationCompletionPosition::ArchQualifier(partial) => { get_arch_qualifier_completions(&partial, architecture_list).await } RelationCompletionPosition::BuildProfile(partial) => { get_build_profile_completions(&partial) } RelationCompletionPosition::Substvar(partial) => get_substvar_completions(&partial), } } /// Get completion items for architecture names. async fn get_architecture_completions( partial: &str, architecture_list: &SharedArchitectureList, ) -> Vec { let negated = partial.starts_with('!'); let prefix = if negated { &partial[1..] } else { partial }; let arches = architecture_list.read().await; arches .iter() .filter(|arch| arch.starts_with(prefix)) .map(|arch| { let label = if negated { format!("!{}", arch) } else { arch.clone() }; CompletionItem { label, kind: Some(CompletionItemKind::VALUE), ..Default::default() } }) .collect() } /// Special architecture qualifier values. const ARCH_QUALIFIER_SPECIALS: &[(&str, &str)] = &[ ("any", "Satisfied by any architecture"), ("native", "Host architecture only"), ]; /// Get completion items for architecture qualifiers (after `:` on a package name). /// /// Offers the special qualifiers `any` and `native`, plus all known architecture names. async fn get_arch_qualifier_completions( partial: &str, architecture_list: &SharedArchitectureList, ) -> Vec { let mut completions: Vec = ARCH_QUALIFIER_SPECIALS .iter() .filter(|(name, _)| name.starts_with(partial)) .map(|&(name, desc)| CompletionItem { label: name.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some(desc.to_string()), ..Default::default() }) .collect(); let arches = architecture_list.read().await; completions.extend( arches .iter() .filter(|arch| arch.starts_with(partial)) .map(|arch| CompletionItem { label: arch.clone(), kind: Some(CompletionItemKind::VALUE), detail: Some("Specific architecture".to_string()), ..Default::default() }), ); completions } /// Known build profile names. /// /// See and dpkg's /// `vendor/default/tupletable`. const BUILD_PROFILES: &[(&str, &str)] = &[ ("cross", "Cross-compilation mode"), ("nobiarch", "Disable multiarch/biarch support"), ("nocheck", "Skip test suites"), ("nodoc", "Skip documentation generation"), ("nogolang", "Skip Go-related build steps"), ("noinsttest", "Skip installed tests"), ("noperl", "Skip Perl-related build steps"), ("nopython", "Skip Python-related build steps"), ("noruby", "Skip Ruby-related build steps"), ("notriggered", "Do not activate triggers"), ("stage1", "Bootstrap stage 1"), ("stage2", "Bootstrap stage 2"), ]; /// Get completion items for build profiles (inside `<...>`). fn get_build_profile_completions(partial: &str) -> Vec { let negated = partial.starts_with('!'); let prefix = if negated { &partial[1..] } else { partial }; BUILD_PROFILES .iter() .filter(|(name, _)| name.starts_with(prefix)) .map(|&(name, desc)| { let label = if negated { format!("!{}", name) } else { name.to_string() }; CompletionItem { label, kind: Some(CompletionItemKind::VALUE), detail: Some(desc.to_string()), ..Default::default() } }) .collect() } /// Known substitution variables used in relationship fields. /// /// See deb-substvars(5). const KNOWN_SUBSTVARS: &[(&str, &str)] = &[ ("shlibs:Depends", "Shared library dependencies"), ("shlibs:Pre-Depends", "Shared library pre-dependencies"), ("shlibs:Suggests", "Shared library suggestions"), ("shlibs:Recommends", "Shared library recommendations"), ("misc:Depends", "Miscellaneous dependencies (debhelper)"), ( "misc:Pre-Depends", "Miscellaneous pre-dependencies (debhelper)", ), ( "misc:Recommends", "Miscellaneous recommendations (debhelper)", ), ("misc:Suggests", "Miscellaneous suggestions (debhelper)"), ("misc:Breaks", "Miscellaneous breaks (debhelper)"), ("misc:Enhances", "Miscellaneous enhances (debhelper)"), ("misc:Provides", "Miscellaneous provides (debhelper)"), ("misc:Conflicts", "Miscellaneous conflicts (debhelper)"), ("misc:Replaces", "Miscellaneous replaces (debhelper)"), ("perl:Depends", "Perl dependencies (dh_perl)"), ("python3:Depends", "Python 3 dependencies (dh_python3)"), ("python3:Provides", "Python 3 provides (dh_python3)"), ("python3:Breaks", "Python 3 breaks (dh_python3)"), ( "sphinxdoc:Depends", "Sphinx documentation dependencies (dh_sphinxdoc)", ), ("binary:Version", "Current binary package version"), ("source:Version", "Current source package version"), ( "source:Upstream-Version", "Upstream version (without Debian revision)", ), ]; /// Get completion items for substitution variables (inside `${...}`). fn get_substvar_completions(partial: &str) -> Vec { KNOWN_SUBSTVARS .iter() .filter(|(name, _)| name.starts_with(partial)) .map(|&(name, desc)| CompletionItem { label: format!("${{{}}}", name), kind: Some(CompletionItemKind::VARIABLE), detail: Some(desc.to_string()), // Insert just the name part — the `${` is already typed and `}` // will be added or is already present. insert_text: Some(format!("{}}}", name)), ..Default::default() }) .collect() } /// Extract the package name from a relationship prefix when in version position. /// /// Parses the prefix using the lossless relations parser and returns the /// name of the last relation (which is the one whose version is being typed). fn extract_package_for_version(prefix: &str) -> Option { let (relations, _errors) = Relations::parse_relaxed(prefix, true); // The last entry's last relation is the one with the version being typed. let last_entry = relations.entries().last()?; let last_relation = last_entry.relations().last()?; Some(last_relation.name()) } /// Get version completions for a package. async fn get_version_completions( prefix: &str, partial: &str, package_cache: &SharedPackageCache, ) -> Vec { let Some(package_name) = extract_package_for_version(prefix) else { return Vec::new(); }; let mut cache = package_cache.write().await; let Some(versions) = cache.load_versions(&package_name).await else { return Vec::new(); }; versions .iter() .filter(|v| v.version.starts_with(partial)) .map(|v| { let suites = v.suites.join(", "); CompletionItem { label: v.version.clone(), kind: Some(CompletionItemKind::VALUE), detail: Some(suites), ..Default::default() } }) .collect() } #[cfg(test)] mod tests { use super::*; use crate::package_cache::TestPackageCache; use std::sync::Arc; fn test_cache() -> SharedPackageCache { TestPackageCache::new_shared(&[ ("cmake", Some("cross-platform make")), ("debhelper-compat", None), ( "dh-python", Some("Debian helper tools for packaging Python"), ), ("libssl-dev", None), ("pkg-config", None), ]) } fn test_arch_list() -> SharedArchitectureList { Arc::new(tokio::sync::RwLock::new(vec![ "amd64".to_string(), "arm64".to_string(), "armhf".to_string(), "i386".to_string(), ])) } #[tokio::test] async fn test_relationship_completions_package_name_empty() { let cache = test_cache(); let completions = get_relationship_completions("", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"debhelper-compat")); assert!(labels.contains(&"cmake")); } #[tokio::test] async fn test_relationship_completions_package_name_prefix() { let cache = test_cache(); let completions = get_relationship_completions("deb", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["debhelper-compat"]); } #[tokio::test] async fn test_relationship_completions_after_comma() { let cache = test_cache(); let completions = get_relationship_completions("libc6 (>= 2.17), cm", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["cmake"]); } #[tokio::test] async fn test_relationship_completions_after_pipe() { let cache = test_cache(); let completions = get_relationship_completions("libfoo | cm", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["cmake"]); } #[tokio::test] async fn test_relationship_completions_version_operator() { let cache = test_cache(); let completions = get_relationship_completions("libc6 (", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec![">=", "<=", "=", ">>", "<<"]); } #[tokio::test] async fn test_relationship_completions_version_operator_partial() { let cache = test_cache(); let completions = get_relationship_completions("libc6 (>", &cache, &test_arch_list()).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec![">=", ">>"]); } #[tokio::test] async fn test_relationship_completions_version_position() { let cache = test_cache(); let completions = get_relationship_completions("libc6 (>= ", &cache, &test_arch_list()).await; assert!(completions.is_empty()); } #[test] fn test_relationship_field_detection() { assert!(is_relationship_field("Depends")); assert!(is_relationship_field("depends")); assert!(is_relationship_field("Build-Depends")); assert!(is_relationship_field("Pre-Depends")); assert!(is_relationship_field("Recommends")); assert!(!is_relationship_field("Section")); assert!(!is_relationship_field("Priority")); assert!(!is_relationship_field("Homepage")); } #[tokio::test] async fn test_relationship_completions_multiline_value() { let cache = test_cache(); let completions = get_relationship_completions("libc6 (>= 2.17),\n dh-py", &cache, &test_arch_list()) .await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["dh-python"]); } #[tokio::test] async fn test_relationship_completions_with_description() { let cache = test_cache(); let completions = get_relationship_completions("cm", &cache, &test_arch_list()).await; assert_eq!(completions.len(), 1); assert_eq!(completions[0].label, "cmake"); assert_eq!( completions[0].detail, Some("cross-platform make".to_string()) ); assert_eq!( completions[0].documentation, Some(Documentation::String("cross-platform make".to_string())) ); } #[tokio::test] async fn test_relationship_completions_without_description() { let cache = test_cache(); let completions = get_relationship_completions("debhelper", &cache, &test_arch_list()).await; assert_eq!(completions.len(), 1); assert_eq!(completions[0].label, "debhelper-compat"); assert_eq!(completions[0].detail, Some("Package".to_string())); assert_eq!(completions[0].documentation, None); } #[test] fn test_determine_relation_position_empty() { assert_eq!( determine_relation_position(""), RelationCompletionPosition::PackageName(String::new()) ); } #[test] fn test_determine_relation_position_leading_space() { assert_eq!( determine_relation_position(" dh"), RelationCompletionPosition::PackageName("dh".to_string()) ); } #[test] fn test_determine_relation_position_leading_space_operator() { assert_eq!( determine_relation_position(" libc6 ("), RelationCompletionPosition::VersionOperator(String::new()) ); } #[test] fn test_determine_relation_position_partial_name() { assert_eq!( determine_relation_position("deb"), RelationCompletionPosition::PackageName("deb".to_string()) ); } #[test] fn test_determine_relation_position_after_open_paren() { assert_eq!( determine_relation_position("libc6 ("), RelationCompletionPosition::VersionOperator(String::new()) ); } #[test] fn test_determine_relation_position_partial_operator() { assert_eq!( determine_relation_position("libc6 (>"), RelationCompletionPosition::VersionOperator(">".to_string()) ); } #[test] fn test_determine_relation_position_after_operator() { assert_eq!( determine_relation_position("libc6 (>= "), RelationCompletionPosition::Version(String::new()) ); } #[test] fn test_determine_relation_position_partial_version() { assert_eq!( determine_relation_position("libc6 (>= 2.1"), RelationCompletionPosition::Version("2.1".to_string()) ); } #[test] fn test_determine_relation_position_after_comma() { assert_eq!( determine_relation_position("libc6, "), RelationCompletionPosition::PackageName(String::new()) ); } #[test] fn test_determine_relation_position_after_complete_relation() { assert_eq!( determine_relation_position("libc6 (>= 2.17), lib"), RelationCompletionPosition::PackageName("lib".to_string()) ); } #[test] fn test_determine_relation_position_after_open_bracket() { assert_eq!( determine_relation_position("libc6 ["), RelationCompletionPosition::Architecture(String::new()) ); } #[test] fn test_determine_relation_position_partial_arch() { assert_eq!( determine_relation_position("libc6 [amd"), RelationCompletionPosition::Architecture("amd".to_string()) ); } #[test] fn test_determine_relation_position_second_arch() { assert_eq!( determine_relation_position("libc6 [amd64 "), RelationCompletionPosition::Architecture(String::new()) ); } #[test] fn test_determine_relation_position_negated_arch() { assert_eq!( determine_relation_position("libc6 [amd64 !arm"), RelationCompletionPosition::Architecture("!arm".to_string()) ); } #[test] fn test_determine_relation_position_after_colon() { assert_eq!( determine_relation_position("libc6:"), RelationCompletionPosition::ArchQualifier(String::new()) ); } #[test] fn test_determine_relation_position_partial_archqual() { assert_eq!( determine_relation_position("libc6:an"), RelationCompletionPosition::ArchQualifier("an".to_string()) ); } #[test] fn test_determine_relation_position_complete_archqual() { assert_eq!( determine_relation_position("libc6:any "), RelationCompletionPosition::PackageName(String::new()) ); } #[tokio::test] async fn test_arch_qualifier_completions_empty() { let cache = test_cache(); let arch_list = test_arch_list(); let completions = get_relationship_completions("libc6:", &cache, &arch_list).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"any")); assert!(labels.contains(&"native")); assert!(labels.contains(&"amd64")); } #[tokio::test] async fn test_arch_qualifier_completions_partial() { let cache = test_cache(); let arch_list = test_arch_list(); let completions = get_relationship_completions("libc6:a", &cache, &arch_list).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"any")); assert!(labels.contains(&"amd64")); assert!(labels.contains(&"arm64")); assert!(labels.contains(&"armhf")); assert!(!labels.contains(&"native")); assert!(!labels.contains(&"i386")); } #[test] fn test_determine_relation_position_after_angle_bracket() { assert_eq!( determine_relation_position("libc6 <"), RelationCompletionPosition::BuildProfile(String::new()) ); } #[test] fn test_determine_relation_position_partial_profile() { assert_eq!( determine_relation_position("libc6 = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"cross")); assert!(labels.contains(&"nocheck")); assert!(labels.contains(&"stage1")); } #[test] fn test_build_profile_completions_partial() { let completions = get_build_profile_completions("no"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"nocheck")); assert!(labels.contains(&"nodoc")); assert!(!labels.contains(&"cross")); assert!(!labels.contains(&"stage1")); } #[test] fn test_build_profile_completions_negated() { let completions = get_build_profile_completions("!no"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"!nocheck")); assert!(labels.contains(&"!nodoc")); assert!(!labels.contains(&"!cross")); } #[tokio::test] async fn test_relationship_completions_build_profile() { let cache = test_cache(); let arch_list = test_arch_list(); let completions = get_relationship_completions("libc6 = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"nocheck")); assert!(!labels.contains(&"cross")); } #[test] fn test_determine_relation_position_after_dollar_brace() { assert_eq!( determine_relation_position("${"), RelationCompletionPosition::Substvar(String::new()) ); } #[test] fn test_determine_relation_position_partial_substvar() { assert_eq!( determine_relation_position("${shlibs"), RelationCompletionPosition::Substvar("shlibs".to_string()) ); } #[test] fn test_determine_relation_position_substvar_with_colon() { assert_eq!( determine_relation_position("${shlibs:Dep"), RelationCompletionPosition::Substvar("shlibs:Dep".to_string()) ); } #[test] fn test_substvar_completions_empty() { let completions = get_substvar_completions(""); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"${shlibs:Depends}")); assert!(labels.contains(&"${misc:Depends}")); } #[test] fn test_substvar_completions_partial() { let completions = get_substvar_completions("shlibs"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"${shlibs:Depends}")); assert!(!labels.contains(&"${misc:Depends}")); } #[test] fn test_substvar_completions_with_colon() { let completions = get_substvar_completions("misc:D"); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"${misc:Depends}")); assert!(!labels.contains(&"${misc:Recommends}")); } #[tokio::test] async fn test_relationship_completions_substvar() { let cache = test_cache(); let arch_list = test_arch_list(); let completions = get_relationship_completions("${shlibs:D", &cache, &arch_list).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"${shlibs:Depends}")); assert!(!labels.contains(&"${misc:Depends}")); } #[tokio::test] async fn test_relationship_completions_substvar_after_comma() { let cache = test_cache(); let arch_list = test_arch_list(); let completions = get_relationship_completions("libc6, ${misc", &cache, &arch_list).await; let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"${misc:Depends}")); } } debian-lsp-0.1.4/src/control/semantic.rs000064400000000000000000000115451046102023000162720ustar 00000000000000//! Semantic token generation for Debian control files. use tower_lsp_server::ls_types::SemanticToken; use super::get_standard_field_name; use crate::deb822::semantic::{generate_tokens, FieldValidator}; /// Field validator for control files pub struct ControlFieldValidator; impl FieldValidator for ControlFieldValidator { fn get_standard_field_name(&self, name: &str) -> Option<&'static str> { get_standard_field_name(name) } } /// Generate semantic tokens for a control file pub fn generate_semantic_tokens( control: &debian_control::lossless::Control, source_text: &str, ) -> Vec { let validator = ControlFieldValidator; generate_tokens(control.as_deb822(), source_text, &validator) } #[cfg(test)] mod tests { use super::*; use crate::deb822::semantic::TokenType; #[test] fn test_generate_semantic_tokens_known_fields() { let text = "Source: test-package\nMaintainer: Test User \n"; let parsed = debian_control::lossless::Control::parse(text); let control = parsed.to_result().expect("Should parse"); let tokens = generate_semantic_tokens(&control, text); // Should have 4 tokens: Source (field), test-package (value), Maintainer (field), value assert_eq!(tokens.len(), 4, "Should have exactly 4 tokens"); // First token: "Source" field name at line 0, char 0 assert_eq!(tokens[0].delta_line, 0, "Source should be on line 0"); assert_eq!(tokens[0].delta_start, 0, "Source should start at char 0"); assert_eq!(tokens[0].length, 6, "Source length should be 6"); assert_eq!( tokens[0].token_type, TokenType::Field as u32, "Source should be classified as Field (known field)" ); // Second token: "test-package" value at line 0, char 8 assert_eq!(tokens[1].delta_line, 0, "Value should be on same line"); assert!( tokens[1].delta_start > 0, "Value should be after field name" ); assert_eq!( tokens[1].token_type, TokenType::Value as u32, "Value should be classified as Value" ); // Third token: "Maintainer" field name at line 1, char 0 assert_eq!(tokens[2].delta_line, 1, "Maintainer should be on line 1"); assert_eq!( tokens[2].delta_start, 0, "Maintainer should start at char 0" ); assert_eq!(tokens[2].length, 10, "Maintainer length should be 10"); assert_eq!( tokens[2].token_type, TokenType::Field as u32, "Maintainer should be classified as Field (known field)" ); // Fourth token: maintainer value assert_eq!(tokens[3].delta_line, 0, "Value should be on same line"); assert_eq!( tokens[3].token_type, TokenType::Value as u32, "Value should be classified as Value" ); } #[test] fn test_generate_semantic_tokens_unknown_field() { let text = "Source: test\nX-Custom-Field: value\n"; let parsed = debian_control::lossless::Control::parse(text); let control = parsed.to_result().expect("Should parse"); let tokens = generate_semantic_tokens(&control, text); assert_eq!(tokens.len(), 4, "Should have 4 tokens"); // First token: "Source" - known field assert_eq!( tokens[0].token_type, TokenType::Field as u32, "Source should be Field (known)" ); // Third token: "X-Custom-Field" - unknown field assert_eq!( tokens[2].token_type, TokenType::UnknownField as u32, "Unknown field should be classified as UnknownField" ); assert_eq!(tokens[2].length, 14, "X-Custom-Field length should be 14"); } #[test] fn test_generate_semantic_tokens_case_insensitive() { let text = "source: test\n"; let parsed = debian_control::lossless::Control::parse(text); let control = parsed.to_result().expect("Should parse"); let tokens = generate_semantic_tokens(&control, text); // "source" (lowercase) should still be recognized as a known field assert_eq!( tokens[0].token_type, TokenType::Field as u32, "Lowercase 'source' should still be classified as Field" ); } #[test] fn test_field_validator() { let validator = ControlFieldValidator; // Known fields assert_eq!(validator.get_standard_field_name("Source"), Some("Source")); assert_eq!(validator.get_standard_field_name("source"), Some("Source")); assert_eq!( validator.get_standard_field_name("Package"), Some("Package") ); // Unknown field assert_eq!(validator.get_standard_field_name("UnknownField"), None); } } debian-lsp-0.1.4/src/copyright/actions.rs000064400000000000000000000211201046102023000164450ustar 00000000000000use crate::position::text_range_to_lsp_range; use crate::workspace::FieldCasingIssue; use text_size::TextRange; use tower_lsp_server::ls_types::*; /// Generate a wrap-and-sort code action for a copyright file /// /// This function creates a code action that wraps and sorts fields in paragraphs /// that overlap with the requested text range. /// /// # Arguments /// * `uri` - The URI of the copyright file /// * `source_text` - The source text of the file /// * `parsed` - The parsed copyright file /// * `text_range` - The text range to operate on /// /// # Returns /// A code action if applicable paragraphs are found, None otherwise pub fn get_wrap_and_sort_action( uri: &Uri, source_text: &str, parsed: &debian_copyright::lossless::Parse, text_range: TextRange, ) -> Option { let copyright = parsed.clone().to_result().ok()?; let mut edits = Vec::new(); // Check if header paragraph is in range if let Some(header) = copyright.header_in_range(text_range) { let para_range = header.as_deb822().text_range(); let formatted_para = header.as_deb822().wrap_and_sort( deb822_lossless::Indentation::Spaces(1), false, Some(79), None, None, ); let lsp_range = text_range_to_lsp_range(source_text, para_range); edits.push(TextEdit { range: lsp_range, new_text: formatted_para.to_string(), }); } // Check each files paragraph in range for files in copyright.iter_files_in_range(text_range) { let para_range = files.as_deb822().text_range(); let formatted_para = files.as_deb822().wrap_and_sort( deb822_lossless::Indentation::Spaces(1), false, Some(79), None, None, ); let lsp_range = text_range_to_lsp_range(source_text, para_range); edits.push(TextEdit { range: lsp_range, new_text: formatted_para.to_string(), }); } // Check each license paragraph in range for license_para in copyright.iter_licenses_in_range(text_range) { let para_range = license_para.as_deb822().text_range(); let formatted_para = license_para.as_deb822().wrap_and_sort( deb822_lossless::Indentation::Spaces(1), false, Some(79), None, None, ); let lsp_range = text_range_to_lsp_range(source_text, para_range); edits.push(TextEdit { range: lsp_range, new_text: formatted_para.to_string(), }); } if edits.is_empty() { return None; } let workspace_edit = WorkspaceEdit { changes: Some(vec![(uri.clone(), edits)].into_iter().collect()), ..Default::default() }; let action = CodeAction { title: "Wrap and sort".to_string(), kind: Some(CodeActionKind::SOURCE_ORGANIZE_IMPORTS), edit: Some(workspace_edit), ..Default::default() }; Some(CodeActionOrCommand::CodeAction(action)) } /// Generate field casing fix actions for a copyright file /// /// # Arguments /// * `uri` - The URI of the copyright file /// * `source_text` - The source text /// * `issues` - The field casing issues found /// * `diagnostics` - The diagnostics from the context /// /// # Returns /// A vector of code actions for fixing field casing pub fn get_field_casing_actions( uri: &Uri, source_text: &str, issues: Vec, diagnostics: &[Diagnostic], ) -> Vec { let mut actions = Vec::new(); for issue in issues { let lsp_range = text_range_to_lsp_range(source_text, issue.field_range); // Check if there's a matching diagnostic in the context let matching_diagnostics = diagnostics .iter() .filter(|d| { d.range == lsp_range && d.code == Some(NumberOrString::String("field-casing".to_string())) }) .cloned() .collect::>(); // Create a code action to fix the casing let edit = TextEdit { range: lsp_range, new_text: issue.standard_name.clone(), }; let workspace_edit = WorkspaceEdit { changes: Some(vec![(uri.clone(), vec![edit])].into_iter().collect()), ..Default::default() }; let action = CodeAction { title: format!( "Fix field casing: {} -> {}", issue.field_name, issue.standard_name ), kind: Some(CodeActionKind::QUICKFIX), edit: Some(workspace_edit), diagnostics: if !matching_diagnostics.is_empty() { Some(matching_diagnostics) } else { None }, ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(action)); } actions } #[cfg(test)] mod tests { use super::*; #[test] fn test_wrap_and_sort_action() { let input = r#"Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: test-package Source: https://example.com/test Files: * Copyright: 2024 Test User License: GPL-3+ License: GPL-3+ This program is free software. "#; let parsed = debian_copyright::lossless::Parse::parse(input); let uri: Uri = "file:///debian/copyright".parse().unwrap(); let text_range = TextRange::new(0.into(), (input.len() as u32).into()); let action = get_wrap_and_sort_action(&uri, input, &parsed, text_range); // Should return a code action assert!(action.is_some()); let CodeActionOrCommand::CodeAction(action) = action.unwrap() else { panic!("Expected CodeAction"); }; assert_eq!(action.title, "Wrap and sort"); assert_eq!(action.kind, Some(CodeActionKind::SOURCE_ORGANIZE_IMPORTS)); // Extract the edits let workspace_edit = action.edit.expect("Should have an edit"); let changes = workspace_edit.changes.expect("Should have changes"); let edits = changes.get(&uri).expect("Should have edits for the URI"); // Should have edits for header, files, and license paragraphs assert_eq!(edits.len(), 3); // Verify the actual formatted output let formatted_header = &edits[0].new_text; let formatted_files = &edits[1].new_text; let formatted_license = &edits[2].new_text; println!("Formatted header:\n{}", formatted_header); println!("Formatted files:\n{}", formatted_files); println!("Formatted license:\n{}", formatted_license); // Check that header is properly formatted (fields sorted alphabetically) assert_eq!( formatted_header, "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: test-package\nSource: https://example.com/test\n" ); // Check that files paragraph is properly formatted assert_eq!( formatted_files, "Files: *\nCopyright: 2024 Test User \nLicense: GPL-3+\n" ); // Check that license paragraph is properly formatted assert_eq!( formatted_license, "License: GPL-3+\n This program is free software.\n" ); } #[test] fn test_field_casing_actions() { let input = r#"format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ upstream-name: test "#; let uri: Uri = "file:///debian/copyright".parse().unwrap(); let issues = vec![ FieldCasingIssue { field_name: "format".to_string(), standard_name: "Format".to_string(), field_range: TextRange::new(0.into(), 6.into()), }, FieldCasingIssue { field_name: "upstream-name".to_string(), standard_name: "Upstream-Name".to_string(), field_range: TextRange::new(76.into(), 89.into()), }, ]; let actions = get_field_casing_actions(&uri, input, issues, &[]); assert_eq!(actions.len(), 2); let CodeActionOrCommand::CodeAction(ref action) = actions[0] else { panic!("Expected CodeAction"); }; assert_eq!(action.title, "Fix field casing: format -> Format"); let CodeActionOrCommand::CodeAction(ref action) = actions[1] else { panic!("Expected CodeAction"); }; assert_eq!( action.title, "Fix field casing: upstream-name -> Upstream-Name" ); } } debian-lsp-0.1.4/src/copyright/completion.rs000064400000000000000000000143021046102023000171620ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Position}; use super::fields::{get_common_licenses, COPYRIGHT_FIELDS}; /// Get completions for a copyright file at the given cursor position. /// /// Uses position-aware completions: if on a field value, returns value /// completions for the current field; otherwise returns field name completions. pub fn get_completions( deb822: &deb822_lossless::Deb822, source_text: &str, position: Position, ) -> Vec { crate::deb822::completion::get_completions( deb822, source_text, position, COPYRIGHT_FIELDS, get_field_value_completions, ) } /// The standard DEP-5 format URL. const DEP5_FORMAT_URL: &str = "https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/"; /// Get value completions for a specific copyright field. fn get_field_value_completions(field_name: &str, prefix: &str) -> Vec { let prefix = prefix.trim(); match field_name.to_lowercase().as_str() { "format" => get_format_completions(prefix), "license" => get_license_completions(prefix), _ => vec![], } } /// Get completion items for the Format field. fn get_format_completions(prefix: &str) -> Vec { if DEP5_FORMAT_URL.starts_with(prefix) { vec![CompletionItem { label: DEP5_FORMAT_URL.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some("DEP-5 copyright format".to_string()), ..Default::default() }] } else { vec![] } } /// Get completion items for common license names, filtered by prefix. fn get_license_completions(prefix: &str) -> Vec { let lower_prefix = prefix.to_lowercase(); get_common_licenses() .iter() .filter(|license| license.to_lowercase().starts_with(&lower_prefix)) .map(|license| CompletionItem { label: license.clone(), kind: Some(CompletionItemKind::VALUE), detail: Some("License name".to_string()), ..Default::default() }) .collect() } #[cfg(test)] mod tests { use super::*; #[test] fn test_get_completions_returns_fields() { let text = "Format: https://example.com\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Cursor on field key → field completions only (no license names mixed in) let completions = get_completions(&deb822, text, Position::new(0, 3)); assert!(completions .iter() .all(|c| c.kind == Some(CompletionItemKind::FIELD))); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"Format")); assert!(labels.contains(&"Files")); assert!(labels.contains(&"License")); assert!(labels.contains(&"Copyright")); } #[test] fn test_field_completions_have_correct_properties() { let text = ""; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(0, 0)); for completion in &completions { assert_eq!(completion.kind, Some(CompletionItemKind::FIELD)); assert!(!completion.label.is_empty()); assert!(completion.detail.is_some()); assert!(completion.documentation.is_some()); assert!(completion.insert_text.as_ref().unwrap().ends_with(": ")); } } #[test] fn test_license_value_completions() { // Only test if /usr/share/common-licenses exists if !std::path::Path::new("/usr/share/common-licenses").exists() { return; } let text = "License: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Cursor on License field value → license name completions let completions = get_completions(&deb822, text, Position::new(0, 9)); assert!(!completions.is_empty()); for completion in &completions { assert_eq!(completion.kind, Some(CompletionItemKind::VALUE)); assert_eq!(completion.detail, Some("License name".to_string())); } let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!( labels .iter() .any(|l| l.contains("GPL") || l.contains("Apache")), "Should contain common licenses, got: {:?}", labels ); } #[test] fn test_license_value_completions_with_prefix() { if !std::path::Path::new("/usr/share/common-licenses").exists() { return; } let text = "License: GPL\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(0, 12)); // All results should match the GPL prefix for completion in &completions { assert!( completion.label.to_lowercase().starts_with("gpl"), "Expected GPL prefix, got: {}", completion.label ); } } #[test] fn test_format_value_completions() { let text = "Format: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(0, 8)); assert_eq!(completions.len(), 1); assert_eq!(completions[0].label, DEP5_FORMAT_URL); assert_eq!(completions[0].kind, Some(CompletionItemKind::VALUE)); } #[test] fn test_format_value_completions_with_non_matching_prefix() { let text = "Format: something-else\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(0, 22)); assert!(completions.is_empty()); } #[test] fn test_unknown_field_value_returns_empty() { let text = "Comment: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions(&deb822, text, Position::new(0, 9)); assert!(completions.is_empty()); } } debian-lsp-0.1.4/src/copyright/detection.rs000064400000000000000000000025211046102023000167670ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a Debian copyright file pub fn is_copyright_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/copyright") || path.ends_with("/debian/copyright") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_copyright_file() { let copyright_paths = vec![ "file:///path/to/debian/copyright", "file:///project/debian/copyright", "file:///copyright", "file:///some/path/copyright", ]; let non_copyright_paths = vec![ "file:///path/to/other.txt", "file:///path/to/copyright.txt", "file:///path/to/mycopyright", "file:///path/to/debian/copyright.backup", "file:///path/to/debian/control", ]; for path in copyright_paths { let uri = path.parse::().unwrap(); assert!( is_copyright_file(&uri), "Should detect copyright file: {}", path ); } for path in non_copyright_paths { let uri = path.parse::().unwrap(); assert!( !is_copyright_file(&uri), "Should not detect as copyright file: {}", path ); } } } debian-lsp-0.1.4/src/copyright/fields.rs000064400000000000000000000121361046102023000162620ustar 00000000000000use std::fs; use std::sync::OnceLock; use crate::deb822::completion::FieldInfo; /// All available Debian copyright file fields (DEP-5 format) pub const COPYRIGHT_FIELDS: &[FieldInfo] = &[ // Header paragraph fields FieldInfo::new("Format", "URI of the format specification (DEP-5)"), FieldInfo::new("Upstream-Name", "Name of the upstream project"), FieldInfo::new("Upstream-Contact", "Contact information for upstream"), FieldInfo::new("Source", "Where the upstream source can be obtained"), FieldInfo::new("Disclaimer", "Disclaimer for non-free packages"), FieldInfo::new("Comment", "Additional information or context"), FieldInfo::new("License", "License for the work"), FieldInfo::new("Copyright", "Copyright holder information"), FieldInfo::new("Files-Excluded", "Files excluded from the source package"), // Files paragraph fields FieldInfo::new("Files", "File patterns covered by this paragraph"), // License paragraph has License and Comment which are already listed above ]; /// Get the standard casing for a field name pub fn get_standard_field_name(field_name: &str) -> Option<&'static str> { crate::deb822::completion::get_standard_field_name(COPYRIGHT_FIELDS, field_name) } /// Cache for common license names loaded from the system static COMMON_LICENSES_CACHE: OnceLock> = OnceLock::new(); /// Load common license names from /usr/share/common-licenses fn load_common_licenses() -> Vec { const COMMON_LICENSES_DIR: &str = "/usr/share/common-licenses"; let mut licenses = Vec::new(); if let Ok(entries) = fs::read_dir(COMMON_LICENSES_DIR) { for entry in entries.flatten() { if let Ok(file_name) = entry.file_name().into_string() { // Skip symlinks that are just shortcuts (GPL, LGPL, GFDL without version) if entry.path().is_symlink() { continue; } licenses.push(file_name); } } } licenses.sort(); licenses } /// Get common license names (cached) pub fn get_common_licenses() -> &'static [String] { COMMON_LICENSES_CACHE.get_or_init(load_common_licenses) } #[cfg(test)] mod tests { use super::*; #[test] fn test_copyright_fields() { assert!(!COPYRIGHT_FIELDS.is_empty()); assert!(COPYRIGHT_FIELDS.len() >= 10); // Test specific fields exist let field_names: Vec<_> = COPYRIGHT_FIELDS.iter().map(|f| f.name).collect(); assert!(field_names.contains(&"Format")); assert!(field_names.contains(&"Files")); assert!(field_names.contains(&"License")); assert!(field_names.contains(&"Copyright")); } #[test] fn test_copyright_field_validity() { for field in COPYRIGHT_FIELDS { assert!(!field.name.is_empty()); assert!(!field.description.is_empty()); assert!( field .name .chars() .all(|c| c.is_ascii_alphanumeric() || c == '-'), "Field {} contains invalid characters", field.name ); } } #[test] fn test_get_standard_field_name() { // Test correct casing - should return the same assert_eq!(get_standard_field_name("Format"), Some("Format")); assert_eq!(get_standard_field_name("Files"), Some("Files")); assert_eq!(get_standard_field_name("License"), Some("License")); // Test incorrect casing - should return the standard form assert_eq!(get_standard_field_name("format"), Some("Format")); assert_eq!(get_standard_field_name("files"), Some("Files")); assert_eq!(get_standard_field_name("license"), Some("License")); assert_eq!(get_standard_field_name("COPYRIGHT"), Some("Copyright")); // Test unknown fields - should return None assert_eq!(get_standard_field_name("UnknownField"), None); assert_eq!(get_standard_field_name("random"), None); } #[test] fn test_get_common_licenses() { let licenses = get_common_licenses(); // Only check for licenses if /usr/share/common-licenses exists // On macOS/Windows this directory won't exist if std::path::Path::new("/usr/share/common-licenses").exists() { assert!(!licenses.is_empty()); // Should have common licenses from /usr/share/common-licenses let license_strs: Vec<&str> = licenses.iter().map(|s| s.as_str()).collect(); assert!( license_strs.contains(&"GPL-2") || license_strs.contains(&"Apache-2.0"), "Should contain common licenses" ); } } #[test] fn test_load_common_licenses() { let licenses = load_common_licenses(); // Only check for licenses if /usr/share/common-licenses exists // On macOS/Windows this directory won't exist if std::path::Path::new("/usr/share/common-licenses").exists() { assert!(!licenses.is_empty()); for license in &licenses { assert!(!license.is_empty()); } } } } debian-lsp-0.1.4/src/copyright/mod.rs000064400000000000000000000004011046102023000155630ustar 00000000000000pub mod actions; pub mod completion; pub mod detection; pub mod fields; pub mod semantic; pub use actions::*; pub use completion::*; pub use detection::is_copyright_file; pub use fields::get_standard_field_name; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/copyright/semantic.rs000064400000000000000000000071431046102023000166210ustar 00000000000000//! Semantic token generation for Debian copyright files. use tower_lsp_server::ls_types::SemanticToken; use super::get_standard_field_name; use crate::deb822::semantic::{generate_tokens, FieldValidator}; /// Field validator for copyright files pub struct CopyrightFieldValidator; impl FieldValidator for CopyrightFieldValidator { fn get_standard_field_name(&self, name: &str) -> Option<&'static str> { get_standard_field_name(name) } } /// Generate semantic tokens for a copyright file pub fn generate_semantic_tokens( copyright: &debian_copyright::lossless::Copyright, source_text: &str, ) -> Vec { let validator = CopyrightFieldValidator; generate_tokens(copyright.as_deb822(), source_text, &validator) } #[cfg(test)] mod tests { use super::*; use crate::deb822::semantic::TokenType; #[test] fn test_generate_semantic_tokens_known_fields() { let text = "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nUpstream-Name: test\n"; let parsed = debian_copyright::lossless::Parse::parse(text); let copyright = parsed.tree(); let tokens = generate_semantic_tokens(©right, text); assert_eq!(tokens.len(), 4, "Should have exactly 4 tokens"); // First token: "Format" field name assert_eq!(tokens[0].delta_line, 0); assert_eq!(tokens[0].delta_start, 0); assert_eq!(tokens[0].length, 6); assert_eq!(tokens[0].token_type, TokenType::Field as u32); // Second token: format value assert_eq!(tokens[1].delta_line, 0); assert_eq!(tokens[1].token_type, TokenType::Value as u32); // Third token: "Upstream-Name" field name assert_eq!(tokens[2].delta_line, 1); assert_eq!(tokens[2].delta_start, 0); assert_eq!(tokens[2].length, 13); assert_eq!(tokens[2].token_type, TokenType::Field as u32); // Fourth token: upstream-name value assert_eq!(tokens[3].delta_line, 0); assert_eq!(tokens[3].token_type, TokenType::Value as u32); } #[test] fn test_generate_semantic_tokens_unknown_field() { let text = "Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/\nX-Custom: value\n"; let parsed = debian_copyright::lossless::Parse::parse(text); let copyright = parsed.tree(); let tokens = generate_semantic_tokens(©right, text); assert_eq!(tokens.len(), 4); // First token: "Format" - known field assert_eq!(tokens[0].token_type, TokenType::Field as u32); // Third token: "X-Custom" - unknown field assert_eq!(tokens[2].token_type, TokenType::UnknownField as u32); assert_eq!(tokens[2].length, 8); } #[test] fn test_generate_semantic_tokens_case_insensitive() { let text = "format: https://example.com\n"; let parsed = debian_copyright::lossless::Parse::parse(text); let copyright = parsed.tree(); let tokens = generate_semantic_tokens(©right, text); assert_eq!(tokens[0].token_type, TokenType::Field as u32); } #[test] fn test_field_validator() { let validator = CopyrightFieldValidator; assert_eq!(validator.get_standard_field_name("Format"), Some("Format")); assert_eq!(validator.get_standard_field_name("format"), Some("Format")); assert_eq!(validator.get_standard_field_name("Files"), Some("Files")); assert_eq!( validator.get_standard_field_name("License"), Some("License") ); assert_eq!(validator.get_standard_field_name("UnknownField"), None); } } debian-lsp-0.1.4/src/deb822/completion.rs000064400000000000000000000315331046102023000161450ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Documentation, Position}; /// A field definition for a deb822-based file format. pub struct FieldInfo { pub name: &'static str, pub description: &'static str, } impl FieldInfo { pub const fn new(name: &'static str, description: &'static str) -> Self { Self { name, description } } } /// What kind of position the cursor is at in a deb822 document. #[derive(Debug, Clone, PartialEq, Eq)] pub enum CursorContext { /// Cursor is on a field key (the part before the colon). FieldKey, /// Cursor is on a field value (after the colon). FieldValue { field_name: String, value_prefix: String, }, /// Cursor is at the start of a line where a new field could be added. StartOfLine, } /// Look up the standard (canonical) casing for a field name. pub fn get_standard_field_name(fields: &[FieldInfo], field_name: &str) -> Option<&'static str> { let lowercase = field_name.to_lowercase(); fields .iter() .find(|f| f.name.to_lowercase() == lowercase) .map(|f| f.name) } /// Generate field name completions from a list of field definitions. pub fn get_field_completions(fields: &[FieldInfo]) -> Vec { fields .iter() .map(|field| CompletionItem { label: field.name.to_string(), kind: Some(CompletionItemKind::FIELD), detail: Some(field.description.to_string()), documentation: Some(Documentation::String(field.description.to_string())), insert_text: Some(format!("{}: ", field.name)), ..Default::default() }) .collect() } /// Determine what kind of completion context the cursor is in. /// /// Returns `None` for positions where no completions make sense /// (e.g. continuation lines, comments, blank lines between paragraphs). pub fn get_cursor_context( deb822: &deb822_lossless::Deb822, source_text: &str, position: Position, ) -> Option { if source_text.is_empty() { return Some(CursorContext::StartOfLine); } let offset = crate::position::try_position_to_offset(source_text, position)?; // If cursor is at column 0 of a new line, it's a position where a new // field can be started, not a continuation of the previous entry. if position.character == 0 { return Some(CursorContext::StartOfLine); } // Find the entry that contains the cursor offset. For incomplete entries // (no colon — the user is still typing a field name), the CST range // excludes the trailing newline, so we use an inclusive end check to // match when the cursor is right at the boundary. let entry = deb822 .paragraphs() .flat_map(|p| p.entries().collect::>()) .find(|entry| { let r = entry.text_range(); if entry.colon_range().is_none() { r.start() <= offset && offset <= r.end() } else { r.start() <= offset && offset < r.end() } }); if let Some(entry) = entry { let field_name = entry.key()?; let colon_range = match entry.colon_range() { Some(r) => r, None => { // Entry has a key but no colon — the user is still typing // a field name. Treat as a field key. return Some(CursorContext::FieldKey); } }; if offset < colon_range.start() { // Before the colon → on the field key return Some(CursorContext::FieldKey); } if offset < colon_range.end() { // On the colon itself → treat as field key return Some(CursorContext::FieldKey); } // After the colon → on the field value // Use the raw source text (not entry.value()) to extract the prefix, // because value_range() spans the raw source including newlines and // continuation-line indentation, while entry.value() strips those. let value_prefix = if let Some(value_range) = entry.value_range() { if offset <= value_range.start() { String::new() } else { let prefix_end = if offset < value_range.end() { offset } else { value_range.end() }; let start: usize = value_range.start().into(); let end: usize = prefix_end.into(); let mut prefix_bytes = end.min(source_text.len()); while !source_text.is_char_boundary(prefix_bytes) { prefix_bytes -= 1; } source_text[start..prefix_bytes].to_string() } } else { String::new() }; return Some(CursorContext::FieldValue { field_name, value_prefix, }); } // Not on any entry — only offer field completions at column 0 // (start of a line where a new field could be written). if position.character == 0 { Some(CursorContext::StartOfLine) } else { None } } /// Get completions for a deb822 document at the given cursor position. /// /// If the cursor is on a field value, calls `value_completer` to get /// value-specific completions (or returns empty if none are defined for /// that field). If the cursor is not on a field value, returns field /// name completions. pub fn get_completions( deb822: &deb822_lossless::Deb822, source_text: &str, position: Position, fields: &[FieldInfo], value_completer: impl Fn(&str, &str) -> Vec, ) -> Vec { match get_cursor_context(deb822, source_text, position) { Some(CursorContext::FieldValue { field_name, value_prefix, }) => value_completer(&field_name, &value_prefix), Some(CursorContext::FieldKey | CursorContext::StartOfLine) => get_field_completions(fields), None => vec![], } } #[cfg(test)] mod tests { use super::*; const TEST_FIELDS: &[FieldInfo] = &[ FieldInfo::new("Source", "Name of the source package"), FieldInfo::new("Package", "Binary package name"), ]; #[test] fn test_get_field_completions() { let completions = get_field_completions(TEST_FIELDS); assert_eq!(completions.len(), 2); for completion in &completions { assert_eq!(completion.kind, Some(CompletionItemKind::FIELD)); assert!(completion.detail.is_some()); assert!(completion.documentation.is_some()); assert!(completion.insert_text.as_ref().unwrap().ends_with(": ")); } assert_eq!(completions[0].label, "Source"); assert_eq!(completions[1].label, "Package"); } #[test] fn test_get_cursor_context_on_value() { let text = "Source: test\nSection: py\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = get_cursor_context(&deb822, text, Position::new(1, 11)).expect("Should have context"); assert_eq!( ctx, CursorContext::FieldValue { field_name: "Section".to_string(), value_prefix: "py".to_string(), } ); } #[test] fn test_get_cursor_context_immediately_after_colon() { let text = "Section: py\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = get_cursor_context(&deb822, text, Position::new(0, 8)).expect("Should have context"); assert_eq!( ctx, CursorContext::FieldValue { field_name: "Section".to_string(), value_prefix: "".to_string(), } ); } #[test] fn test_get_cursor_context_on_field_key() { let text = "Source: test\nSection: py\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = get_cursor_context(&deb822, text, Position::new(1, 3)).expect("Should have context"); assert_eq!(ctx, CursorContext::FieldKey); } #[test] fn test_get_cursor_context_empty_text() { let text = ""; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let ctx = get_cursor_context(&deb822, text, Position::new(0, 0)).expect("Should have context"); assert_eq!(ctx, CursorContext::StartOfLine); } #[test] fn test_get_completions_on_value_with_completer() { let text = "Source: te\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions( &deb822, text, Position::new(0, 10), TEST_FIELDS, |field, _prefix| { if field == "Source" { vec![CompletionItem { label: "test-value".to_string(), ..Default::default() }] } else { vec![] } }, ); assert_eq!(completions.len(), 1); assert_eq!(completions[0].label, "test-value"); } #[test] fn test_get_completions_falls_back_to_fields() { let text = "Source: test\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Cursor on field key area → no value context → field completions let completions = get_completions( &deb822, text, Position::new(0, 2), TEST_FIELDS, |_, _| vec![], ); assert_eq!(completions.len(), 2); assert_eq!(completions[0].label, "Source"); assert_eq!(completions[1].label, "Package"); } #[test] fn test_get_completions_no_value_completions_returns_empty() { let text = "Source: test\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Cursor on value, completer returns empty → empty (not field completions) let completions = get_completions( &deb822, text, Position::new(0, 10), TEST_FIELDS, |_, _| vec![], ); assert!(completions.is_empty()); } #[test] fn test_get_cursor_context_multiline_value() { let text = "Build-Depends:\n debhelper-compat (= 13),\n pkg-co\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Cursor at end of "pkg-co" on line 2, column 7 let ctx = get_cursor_context(&deb822, text, Position::new(2, 7)).expect("Should have context"); match ctx { CursorContext::FieldValue { field_name, value_prefix, } => { assert_eq!(field_name, "Build-Depends"); // Should include the full raw value text up to cursor assert!( value_prefix.contains("debhelper-compat"), "prefix should contain prior relations: {:?}", value_prefix ); assert!( value_prefix.ends_with("pkg-co"), "prefix should end with partial name: {:?}", value_prefix ); } other => panic!("Expected FieldValue, got {:?}", other), } } #[test] fn test_get_cursor_context_partial_field_name_no_colon() { let text = "Source: test\nMai"; let deb822 = deb822_lossless::Deb822::parse(text).tree(); let ctx = get_cursor_context(&deb822, text, Position::new(1, 3)).expect("Should have context"); assert_eq!(ctx, CursorContext::FieldKey); } #[test] fn test_get_cursor_context_empty_new_line_after_entry() { let text = "Source: test\n"; let deb822 = deb822_lossless::Deb822::parse(text).tree(); let ctx = get_cursor_context(&deb822, text, Position::new(1, 0)).expect("Should have context"); assert_eq!(ctx, CursorContext::StartOfLine); } #[test] fn test_get_cursor_context_typing_single_char_on_new_line() { let text = "Source: test\nM"; let deb822 = deb822_lossless::Deb822::parse(text).tree(); let ctx = get_cursor_context(&deb822, text, Position::new(1, 1)).expect("Should have context"); assert_eq!(ctx, CursorContext::FieldKey); } #[test] fn test_partial_field_between_existing_fields() { let text = "Source: debian-codemods\nSection: devel\nHomepa\nPriority: optional\n"; let deb822 = deb822_lossless::Deb822::parse(text).tree(); let ctx = get_cursor_context(&deb822, text, Position::new(2, 6)); assert!(ctx.is_some(), "Should have context"); assert_eq!(ctx.unwrap(), CursorContext::FieldKey); } } debian-lsp-0.1.4/src/deb822/mod.rs000064400000000000000000000001361046102023000145460ustar 00000000000000//! Generic utilities for deb822-lossless based files. pub mod completion; pub mod semantic; debian-lsp-0.1.4/src/deb822/semantic.rs000064400000000000000000000216451046102023000156020ustar 00000000000000//! Generic semantic token generation for deb822 files. //! //! This module provides the core logic for generating semantic tokens from //! deb822-lossless parse trees. File-type-specific modules (control, copyright) //! use this by providing field validation callbacks. use deb822_lossless::{Deb822, SyntaxKind}; use rowan::ast::AstNode; use tower_lsp_server::ls_types::SemanticToken; use crate::position::offset_to_position; /// Semantic token types reported by the server. /// /// The discriminant values must match the order in the `token_types` legend /// registered in `main.rs` `initialize()`. #[derive(Debug, Clone, Copy, PartialEq, Eq)] #[repr(u32)] pub enum TokenType { // deb822 types Field = 0, UnknownField = 1, Value = 2, Comment = 3, // Changelog-specific types ChangelogPackage = 4, ChangelogVersion = 5, ChangelogDistribution = 6, ChangelogUrgency = 7, ChangelogMaintainer = 8, ChangelogTimestamp = 9, ChangelogMetadataValue = 10, } /// Token modifier bit flags pub mod token_modifier { pub const DECLARATION: u32 = 1 << 0; } /// Field validation callback pub trait FieldValidator { /// Check if a field name is valid and get its standard casing fn get_standard_field_name(&self, name: &str) -> Option<&'static str>; } /// Helper for building semantic token arrays pub struct SemanticTokensBuilder { tokens: Vec, prev_line: u32, prev_char: u32, } impl SemanticTokensBuilder { pub fn new() -> Self { Self { tokens: Vec::new(), prev_line: 0, prev_char: 0, } } /// Add a token at the given position pub fn push( &mut self, line: u32, start_char: u32, length: u32, token_type: TokenType, token_modifiers: u32, ) { let delta_line = line - self.prev_line; let delta_start = if delta_line == 0 { start_char - self.prev_char } else { start_char }; self.tokens.push(SemanticToken { delta_line, delta_start, length, token_type: token_type as u32, token_modifiers_bitset: token_modifiers, }); self.prev_line = line; self.prev_char = start_char; } pub fn build(self) -> Vec { self.tokens } } impl Default for SemanticTokensBuilder { fn default() -> Self { Self::new() } } /// Generate semantic tokens for a deb822 file pub fn generate_tokens( deb822: &Deb822, source_text: &str, validator: &V, ) -> Vec { let mut builder = SemanticTokensBuilder::new(); // Single pass through the syntax tree for element in deb822.syntax().descendants_with_tokens() { if let rowan::NodeOrToken::Token(token) = element { match token.kind() { SyntaxKind::COMMENT => { let range = token.text_range(); let start_pos = offset_to_position(source_text, range.start()); let length = (usize::from(range.end()) - usize::from(range.start())) as u32; builder.push( start_pos.line, start_pos.character, length, TokenType::Comment, 0, ); } SyntaxKind::KEY => { let range = token.text_range(); let start_pos = offset_to_position(source_text, range.start()); let key = token.text(); let length = key.len() as u32; // Check if field is known let token_type = if validator.get_standard_field_name(key).is_some() { TokenType::Field } else { TokenType::UnknownField }; builder.push( start_pos.line, start_pos.character, length, token_type, token_modifier::DECLARATION, ); } SyntaxKind::VALUE => { let range = token.text_range(); let start_pos = offset_to_position(source_text, range.start()); let length = (usize::from(range.end()) - usize::from(range.start())) as u32; if length > 0 { builder.push( start_pos.line, start_pos.character, length, TokenType::Value, 0, ); } } _ => {} } } } builder.build() } #[cfg(test)] mod tests { use super::*; struct TestValidator; impl FieldValidator for TestValidator { fn get_standard_field_name(&self, name: &str) -> Option<&'static str> { if name.eq_ignore_ascii_case("Source") { Some("Source") } else if name.eq_ignore_ascii_case("Package") { Some("Package") } else { None } } } #[test] fn test_generate_tokens_known_and_unknown_fields() { let text = "Source: foo\nX-Custom: bar\n"; let parsed = deb822_lossless::Deb822::parse(text); let deb822 = parsed.tree(); let validator = TestValidator; let tokens = generate_tokens(&deb822, text, &validator); // Source (known field), value, X-Custom (unknown field), value assert_eq!(tokens.len(), 4); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 6); // "Source" assert_eq!(tokens[1].token_type, TokenType::Value as u32); assert_eq!(tokens[2].token_type, TokenType::UnknownField as u32); assert_eq!(tokens[2].length, 8); // "X-Custom" assert_eq!(tokens[3].token_type, TokenType::Value as u32); } #[test] fn test_generate_tokens_comments() { let text = "# This is a comment\nSource: foo\n"; let parsed = deb822_lossless::Deb822::parse(text); let deb822 = parsed.tree(); let validator = TestValidator; let tokens = generate_tokens(&deb822, text, &validator); assert_eq!(tokens[0].token_type, TokenType::Comment as u32); assert_eq!(tokens[1].token_type, TokenType::Field as u32); } #[test] fn test_generate_tokens_multi_paragraph() { let text = "Source: foo\n\nPackage: bar\n"; let parsed = deb822_lossless::Deb822::parse(text); let deb822 = parsed.tree(); let validator = TestValidator; let tokens = generate_tokens(&deb822, text, &validator); // Source, value, Package, value assert_eq!(tokens.len(), 4); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 6); // "Source" assert_eq!(tokens[2].token_type, TokenType::Field as u32); assert_eq!(tokens[2].length, 7); // "Package" } #[test] fn test_generate_tokens_empty() { let text = ""; let parsed = deb822_lossless::Deb822::parse(text); let deb822 = parsed.tree(); let validator = TestValidator; let tokens = generate_tokens(&deb822, text, &validator); assert_eq!(tokens.len(), 0); } #[test] fn test_generate_tokens_field_modifiers() { let text = "Source: foo\n"; let parsed = deb822_lossless::Deb822::parse(text); let deb822 = parsed.tree(); let validator = TestValidator; let tokens = generate_tokens(&deb822, text, &validator); // Field tokens should have DECLARATION modifier assert_eq!( tokens[0].token_modifiers_bitset, token_modifier::DECLARATION ); // Value tokens should have no modifiers assert_eq!(tokens[1].token_modifiers_bitset, 0); } #[test] fn test_semantic_tokens_builder() { let mut builder = SemanticTokensBuilder::new(); // Add a token on line 0 builder.push(0, 0, 6, TokenType::Field, 0); // Add another token on the same line builder.push(0, 8, 4, TokenType::Value, 0); // Add a token on line 1 builder.push(1, 0, 7, TokenType::Field, 0); let tokens = builder.build(); assert_eq!(tokens.len(), 3); // First token assert_eq!(tokens[0].delta_line, 0); assert_eq!(tokens[0].delta_start, 0); assert_eq!(tokens[0].length, 6); // Second token (same line) assert_eq!(tokens[1].delta_line, 0); assert_eq!(tokens[1].delta_start, 8); // Third token (new line) assert_eq!(tokens[2].delta_line, 1); assert_eq!(tokens[2].delta_start, 0); } } debian-lsp-0.1.4/src/main.rs000064400000000000000000000674221046102023000137400ustar 00000000000000//! Debian Language Server Protocol implementation. #![deny(missing_docs)] #![deny(unsafe_code)] use std::sync::Arc; use tokio::sync::Mutex; use tower_lsp_server::jsonrpc::Result; use tower_lsp_server::ls_types::*; use tower_lsp_server::{Client, LanguageServer, LspService, Server}; mod architecture; mod changelog; mod control; mod copyright; mod deb822; mod package_cache; mod position; mod source_format; mod tests; mod upstream_metadata; mod watch; mod workspace; use position::{text_range_to_lsp_range, try_lsp_range_to_text_range}; use std::collections::HashMap; use workspace::Workspace; /// Debian file type #[derive(Debug, Clone, Copy, PartialEq, Eq)] enum FileType { /// debian/control file Control, /// debian/copyright file Copyright, /// debian/watch file Watch, /// debian/tests/control file TestsControl, /// debian/changelog file Changelog, /// debian/source/format file SourceFormat, /// debian/upstream/metadata file UpstreamMetadata, } impl FileType { /// Detect the file type from a URI fn detect(uri: &Uri) -> Option { if control::is_control_file(uri) { Some(Self::Control) } else if copyright::is_copyright_file(uri) { Some(Self::Copyright) } else if watch::is_watch_file(uri) { Some(Self::Watch) } else if tests::is_tests_control_file(uri) { Some(Self::TestsControl) } else if changelog::is_changelog_file(uri) { Some(Self::Changelog) } else if source_format::is_source_format_file(uri) { Some(Self::SourceFormat) } else if upstream_metadata::is_upstream_metadata_file(uri) { Some(Self::UpstreamMetadata) } else { None } } } /// Information about an open file #[derive(Clone, Copy)] struct FileInfo { /// The workspace's source file ID source_file: workspace::SourceFile, /// The detected file type file_type: FileType, } struct Backend { client: Client, workspace: Arc>, files: Arc>>, package_cache: package_cache::SharedPackageCache, architecture_list: architecture::SharedArchitectureList, } impl Backend { fn collect_diagnostics( source_file: workspace::SourceFile, file_type: FileType, workspace: &Workspace, ) -> Option> { match file_type { FileType::Control => { let source_text = workspace.source_text(source_file); let parsed = workspace.get_parsed_control(source_file); Some(control::diagnostics::get_diagnostics(&source_text, &parsed)) } FileType::Copyright => Some(workspace.get_copyright_diagnostics(source_file)), FileType::Watch | FileType::TestsControl | FileType::Changelog | FileType::SourceFormat | FileType::UpstreamMetadata => None, } } } impl LanguageServer for Backend { async fn initialize(&self, _: InitializeParams) -> Result { Ok(InitializeResult { capabilities: ServerCapabilities { text_document_sync: Some(TextDocumentSyncCapability::Kind( TextDocumentSyncKind::FULL, )), completion_provider: Some(CompletionOptions { resolve_provider: Some(false), trigger_characters: Some(vec![ ":".to_string(), " ".to_string(), "(".to_string(), "[".to_string(), "<".to_string(), "$".to_string(), "=".to_string(), ",".to_string(), ]), work_done_progress_options: Default::default(), all_commit_characters: None, completion_item: None, }), code_action_provider: Some(CodeActionProviderCapability::Simple(true)), semantic_tokens_provider: Some( SemanticTokensServerCapabilities::SemanticTokensOptions( SemanticTokensOptions { work_done_progress_options: WorkDoneProgressOptions::default(), legend: SemanticTokensLegend { token_types: vec![ SemanticTokenType::new("debianField"), SemanticTokenType::new("debianUnknownField"), SemanticTokenType::new("debianValue"), SemanticTokenType::new("debianComment"), SemanticTokenType::new("changelogPackage"), SemanticTokenType::new("changelogVersion"), SemanticTokenType::new("changelogDistribution"), SemanticTokenType::new("changelogUrgency"), SemanticTokenType::new("changelogMaintainer"), SemanticTokenType::new("changelogTimestamp"), SemanticTokenType::new("changelogMetadataValue"), ], token_modifiers: vec![SemanticTokenModifier::DECLARATION], }, range: Some(false), full: Some(SemanticTokensFullOptions::Bool(true)), }, ), ), ..Default::default() }, ..Default::default() }) } async fn initialized(&self, _: InitializedParams) { self.client .log_message(MessageType::INFO, "Debian LSP initialized!") .await; } async fn shutdown(&self) -> Result<()> { Ok(()) } async fn did_open(&self, params: DidOpenTextDocumentParams) { self.client .log_message( MessageType::INFO, format!("file opened: {:?}", params.text_document.uri), ) .await; // Detect file type once let Some(file_type) = FileType::detect(¶ms.text_document.uri) else { return; }; let mut workspace = self.workspace.lock().await; let source_file = workspace.update_file( params.text_document.uri.clone(), params.text_document.text.clone(), ); let mut files = self.files.lock().await; files.insert( params.text_document.uri.clone(), FileInfo { source_file, file_type, }, ); if let Some(diagnostics) = Self::collect_diagnostics(source_file, file_type, &workspace) { drop(workspace); self.client .publish_diagnostics(params.text_document.uri.clone(), diagnostics, None) .await; } } async fn did_change(&self, params: DidChangeTextDocumentParams) { self.client .log_message( MessageType::INFO, format!("file changed: {:?}", params.text_document.uri), ) .await; // Get or detect the file type let mut files = self.files.lock().await; let file_type = files .get(¶ms.text_document.uri) .map(|info| info.file_type) .or_else(|| FileType::detect(¶ms.text_document.uri)); let Some(file_type) = file_type else { return; }; // Apply the content changes let Some(changes) = params.content_changes.first() else { return; }; let mut workspace = self.workspace.lock().await; let source_file = workspace.update_file(params.text_document.uri.clone(), changes.text.clone()); files.insert( params.text_document.uri.clone(), FileInfo { source_file, file_type, }, ); if let Some(diagnostics) = Self::collect_diagnostics(source_file, file_type, &workspace) { drop(workspace); self.client .publish_diagnostics(params.text_document.uri.clone(), diagnostics, None) .await; } } async fn completion(&self, params: CompletionParams) -> Result> { let uri = params.text_document_position.text_document.uri; let position = params.text_document_position.position; // Look up the file type from our cache let files = self.files.lock().await; let file_info = files .get(&uri) .map(|info| (info.file_type, info.source_file)); drop(files); // Release the lock let completions = match file_info { Some((FileType::Control, source_file)) => { let workspace = self.workspace.lock().await; let source_text = workspace.source_text(source_file); let parsed = workspace.get_parsed_control(source_file); // Check if cursor is on a field value to try async relationship completions let cursor_context = deb822::completion::get_cursor_context( parsed.tree().as_deb822(), &source_text, position, ); drop(workspace); if let Some(deb822::completion::CursorContext::FieldValue { field_name, value_prefix, }) = &cursor_context { // Try async completions (relationship fields via package cache) if let Some(async_completions) = control::get_async_field_value_completions( field_name, value_prefix, &self.package_cache, &self.architecture_list, ) .await { async_completions } else { // Fall back to sync completions (Section, Priority, etc.) control::get_field_value_completions(field_name, value_prefix) } } else { // Not on a field value — get field name completions let workspace = self.workspace.lock().await; let source_text = workspace.source_text(source_file); let parsed = workspace.get_parsed_control(source_file); control::get_completions(parsed.tree().as_deb822(), &source_text, position) } } Some((FileType::Copyright, source_file)) => { let workspace = self.workspace.lock().await; let source_text = workspace.source_text(source_file); let parsed = workspace.get_parsed_copyright(source_file); let copyright = parsed.to_copyright(); copyright::get_completions(copyright.as_deb822(), &source_text, position) } Some((FileType::Watch, source_file)) => { let workspace = self.workspace.lock().await; let parsed = workspace.get_parsed_watch(source_file); let source_text = workspace.source_text(source_file); let wf = parsed.to_watch_file(); match &wf { debian_watch::parse::ParsedWatchFile::LineBased(wf) => { watch::get_linebased_completions(&uri, wf, &source_text, position) } debian_watch::parse::ParsedWatchFile::Deb822(_) => { deb822_lossless::Deb822::parse(&source_text) .to_result() .map(|d| watch::get_completions_deb822(&d, &source_text, position)) .unwrap_or_default() } } } Some((FileType::TestsControl, _)) => tests::get_completions(&uri, position), Some((FileType::Changelog, source_file)) => { let workspace = self.workspace.lock().await; let source_text = workspace.source_text(source_file); let parsed = workspace.get_parsed_changelog(source_file); changelog::get_completions(&parsed, &source_text, position) } Some((FileType::SourceFormat, _)) => source_format::get_completions(&uri, position), Some((FileType::UpstreamMetadata, source_file)) => { let workspace = self.workspace.lock().await; let source_text = workspace.source_text(source_file); upstream_metadata::get_completions(&source_text, position) } None => Vec::new(), }; if completions.is_empty() { Ok(None) } else { Ok(Some(CompletionResponse::Array(completions))) } } async fn code_action(&self, params: CodeActionParams) -> Result> { let workspace = self.workspace.lock().await; let files = self.files.lock().await; let file_info = match files.get(¶ms.text_document.uri) { Some(info) => info, None => return Ok(None), }; // Only control, copyright, and changelog files support code actions for now match file_info.file_type { FileType::Control | FileType::Copyright | FileType::Changelog => {} _ => return Ok(None), } let source_text = workspace.source_text(file_info.source_file); let mut actions = Vec::new(); // Check for field casing issues - only process fields in the requested range let Some(text_range) = try_lsp_range_to_text_range(&source_text, ¶ms.range) else { return Ok(None); }; match file_info.file_type { FileType::Control => { // Add wrap-and-sort action let parsed = workspace.get_parsed_control(file_info.source_file); if let Some(action) = control::get_wrap_and_sort_action( ¶ms.text_document.uri, &source_text, &parsed, text_range, ) { actions.push(action); } // Add field casing fixes let issues = control::diagnostics::find_field_casing_issues(&parsed, Some(text_range)); actions.extend(control::get_field_casing_actions( ¶ms.text_document.uri, &source_text, issues, ¶ms.context.diagnostics, )); } FileType::Copyright => { // Add wrap-and-sort action let parsed = workspace.get_parsed_copyright(file_info.source_file); if let Some(action) = copyright::get_wrap_and_sort_action( ¶ms.text_document.uri, &source_text, &parsed, text_range, ) { actions.push(action); } // Add field casing fixes let issues = workspace .find_copyright_field_casing_issues(file_info.source_file, Some(text_range)); actions.extend(copyright::get_field_casing_actions( ¶ms.text_document.uri, &source_text, issues, ¶ms.context.diagnostics, )); } FileType::Changelog => { // Add action to create a new changelog entry let parsed = workspace.get_parsed_changelog(file_info.source_file); let changelog = parsed.tree(); match changelog::generate_new_changelog_entry(&changelog) { Ok(new_entry) => { // Insert the new entry at the beginning of the file let edit = TextEdit { range: Range { start: Position { line: 0, character: 0, }, end: Position { line: 0, character: 0, }, }, new_text: new_entry, }; let workspace_edit = WorkspaceEdit { changes: Some( vec![(params.text_document.uri.clone(), vec![edit])] .into_iter() .collect(), ), ..Default::default() }; let action = CodeAction { title: "Add new changelog entry".to_string(), kind: Some(CodeActionKind::REFACTOR), edit: Some(workspace_edit), ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(action)); } Err(_) => { // If we can't generate a new entry, don't add the action } } // Check for UNRELEASED entries in the requested range and offer "Mark for upload" let unreleased_entries = workspace.find_unreleased_entries_in_range(file_info.source_file, text_range); for info in unreleased_entries { let lsp_range = text_range_to_lsp_range(&source_text, info.unreleased_range); let edit = TextEdit { range: lsp_range, new_text: info.target_distribution.clone(), }; let workspace_edit = WorkspaceEdit { changes: Some( vec![(params.text_document.uri.clone(), vec![edit])] .into_iter() .collect(), ), ..Default::default() }; let action = CodeAction { title: format!("Mark for upload to {}", info.target_distribution), kind: Some(CodeActionKind::REFACTOR), edit: Some(workspace_edit), ..Default::default() }; actions.push(CodeActionOrCommand::CodeAction(action)); } } _ => unreachable!(), } if actions.is_empty() { Ok(None) } else { Ok(Some(actions)) } } async fn semantic_tokens_full( &self, params: SemanticTokensParams, ) -> Result> { let uri = ¶ms.text_document.uri; let workspace = self.workspace.lock().await; let files = self.files.lock().await; let file = match files.get(uri) { Some(f) => *f, None => return Ok(None), }; drop(files); let source_text = workspace.source_text(file.source_file); let tokens = match file.file_type { FileType::Control => { let parsed = workspace.get_parsed_control(file.source_file); let control = parsed.tree(); control::generate_semantic_tokens(&control, &source_text) } FileType::Copyright => { let parsed = workspace.get_parsed_copyright(file.source_file); let copyright = parsed.tree(); copyright::generate_semantic_tokens(©right, &source_text) } FileType::Changelog => { let parsed = workspace.get_parsed_changelog(file.source_file); changelog::generate_semantic_tokens(&parsed, &source_text) } FileType::Watch => { let parsed = workspace.get_parsed_watch(file.source_file); watch::generate_semantic_tokens(&parsed, &source_text) } FileType::TestsControl => tests::generate_semantic_tokens(&source_text), FileType::UpstreamMetadata => upstream_metadata::generate_semantic_tokens(&source_text), // TODO: Implement semantic tokens for other file types _ => vec![], }; if tokens.is_empty() { Ok(None) } else { Ok(Some(SemanticTokensResult::Tokens(SemanticTokens { result_id: None, data: tokens, }))) } } } #[tokio::main] async fn main() { let stdin = tokio::io::stdin(); let stdout = tokio::io::stdout(); // Load package cache in background let package_cache = package_cache::new_shared_cache(); let cache_for_loading = package_cache.clone(); tokio::spawn(async move { package_cache::stream_packages_into(&cache_for_loading).await; }); // Load architecture list in background let architecture_list = architecture::new_shared_list(); let arch_for_loading = architecture_list.clone(); tokio::spawn(async move { architecture::stream_into(&arch_for_loading).await; }); let (service, socket) = LspService::new(|client| Backend { client, workspace: Arc::new(Mutex::new(Workspace::new())), files: Arc::new(Mutex::new(HashMap::new())), package_cache: package_cache.clone(), architecture_list: architecture_list.clone(), }); Server::new(stdin, stdout, socket).serve(service).await; } #[cfg(test)] mod main_tests { use super::*; #[test] fn test_completion_returns_control_completions() { let text = "Source: test\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = control::get_completions(&deb822, text, Position::new(0, 3)); assert!(!completions.is_empty()); assert!(completions.iter().any(|c| c.label == "Source")); } #[test] fn test_workspace_integration() { // Test that the workspace can parse control files let mut workspace = workspace::Workspace::new(); let url = str::parse("file:///debian/control").unwrap(); let content = "source: test-package\nMaintainer: Test \n"; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_control(file); // Should parse correctly assert!(parsed.errors().is_empty()); if let Ok(control) = parsed.to_result() { let mut field_names = Vec::new(); for paragraph in control.as_deb822().paragraphs() { for entry in paragraph.entries() { if let Some(name) = entry.key() { field_names.push(name); } } } assert!(field_names.contains(&"source".to_string())); assert!(field_names.contains(&"Maintainer".to_string())); } } #[test] fn test_field_casing_detection() { // Test that we can detect incorrect field casing use control::get_standard_field_name; // Test correct casing - should return the same assert_eq!(get_standard_field_name("Source"), Some("Source")); assert_eq!(get_standard_field_name("Package"), Some("Package")); assert_eq!(get_standard_field_name("Maintainer"), Some("Maintainer")); // Test incorrect casing - should return the standard form assert_eq!(get_standard_field_name("source"), Some("Source")); assert_eq!(get_standard_field_name("package"), Some("Package")); assert_eq!(get_standard_field_name("maintainer"), Some("Maintainer")); assert_eq!(get_standard_field_name("MAINTAINER"), Some("Maintainer")); // Test unknown fields - should return None assert_eq!(get_standard_field_name("UnknownField"), None); assert_eq!(get_standard_field_name("random"), None); } #[test] fn test_changelog_action_generation() { // Test that we can generate a new changelog entry let changelog_content = r#"test-package (1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_content); let changelog = parsed.tree(); let result = changelog::generate_new_changelog_entry(&changelog); assert!(result.is_ok(), "Should successfully generate entry"); let new_entry = result.unwrap(); // Parse the lines to verify exact structure let lines: Vec<&str> = new_entry.lines().collect(); assert!(lines.len() >= 5, "Should have at least 5 lines"); // Check the header line exactly (version is incremented, uses UNRELEASED) assert_eq!( lines[0], "test-package (1.0-2) UNRELEASED; urgency=medium", "First line should be header with incremented version and UNRELEASED" ); // Check empty line after header assert_eq!(lines[1], "", "Second line should be empty"); // Check bullet point line assert_eq!(lines[2], " * ", "Third line should be bullet point"); // Check empty line before signature assert_eq!(lines[3], "", "Fourth line should be empty"); // Check signature line starts with proper format assert!( lines[4].starts_with(" -- "), "Fifth line should start with signature marker, got: {}", lines[4] ); } #[test] fn test_changelog_version_increment_multiple_revisions() { // Test the version increment logic with different versions let changelog_text = r#"mypackage (2.5-3) unstable; urgency=low * Some changes. -- Jane Smith Tue, 15 Feb 2025 10:30:00 +0000 "#; let parsed = debian_changelog::ChangeLog::parse(changelog_text); let changelog = parsed.tree(); let result = changelog::generate_new_changelog_entry(&changelog); assert!(result.is_ok(), "Should successfully generate entry"); let new_entry = result.unwrap(); let lines: Vec<&str> = new_entry.lines().collect(); // Check exact version increment (3 -> 4) with UNRELEASED assert_eq!( lines[0], "mypackage (2.5-4) UNRELEASED; urgency=medium", "Should increment debian revision from 3 to 4 with UNRELEASED" ); } #[test] fn test_changelog_file_type_detection() { // Test that we correctly detect changelog files let changelog_uri: Uri = str::parse("file:///path/to/debian/changelog").unwrap(); let control_uri: Uri = str::parse("file:///path/to/debian/control").unwrap(); assert_eq!(FileType::detect(&changelog_uri), Some(FileType::Changelog)); assert_eq!(FileType::detect(&control_uri), Some(FileType::Control)); } #[test] fn test_upstream_metadata_file_type_detection() { let metadata_uri: Uri = str::parse("file:///path/to/debian/upstream/metadata").unwrap(); let non_metadata_uri: Uri = str::parse("file:///path/to/upstream/metadata").unwrap(); assert_eq!( FileType::detect(&metadata_uri), Some(FileType::UpstreamMetadata) ); assert_eq!(FileType::detect(&non_metadata_uri), None); } } debian-lsp-0.1.4/src/package_cache.rs000064400000000000000000000175451046102023000155330ustar 00000000000000use std::collections::HashMap; use std::sync::Arc; use tokio::io::{AsyncBufReadExt, BufReader}; use tokio::process::Command; use tokio::sync::RwLock; /// Version information for a package. #[derive(Debug, Clone)] pub struct VersionInfo { /// The version string. pub version: String, /// The suites this version is available in. pub suites: Vec, } /// Access to the package cache. #[async_trait::async_trait] pub trait PackageCache: Send + Sync { /// Get packages whose names start with a given prefix, sorted. fn get_packages_with_prefix(&self, prefix: &str) -> Vec; /// Get the cached short description for a package. fn get_description(&self, package: &str) -> Option<&str>; /// Load and cache versions for a package. async fn load_versions(&mut self, package: &str) -> Option<&[VersionInfo]>; /// Insert a package name with its short description into the cache. fn insert_package(&mut self, name: String, description: String); } /// Thread-safe shared package cache. pub type SharedPackageCache = Arc>; /// Package cache backed by `apt-cache`. pub struct AptPackageCache { /// All available package names (sorted). packages: Vec, /// Package descriptions (package name -> short description). descriptions: HashMap, /// Cached versions for specific packages. versions: HashMap>, } impl AptPackageCache { /// Create a new empty cache. pub fn new() -> Self { Self { packages: Vec::new(), descriptions: HashMap::new(), versions: HashMap::new(), } } } #[async_trait::async_trait] impl PackageCache for AptPackageCache { fn get_packages_with_prefix(&self, prefix: &str) -> Vec { let prefix_lower = prefix.to_ascii_lowercase(); self.packages .iter() .filter(|p| p.starts_with(&prefix_lower)) .cloned() .collect() } fn get_description(&self, package: &str) -> Option<&str> { self.descriptions.get(package).map(|s| s.as_str()) } async fn load_versions(&mut self, package: &str) -> Option<&[VersionInfo]> { if self.versions.contains_key(package) { return self.versions.get(package).map(|v| v.as_slice()); } match Command::new("apt-cache") .arg("madison") .arg(package) .output() .await { Ok(output) if output.status.success() => { let text = String::from_utf8_lossy(&output.stdout); let mut version_suites: HashMap> = HashMap::new(); for line in text.lines() { let parts: Vec<&str> = line.split('|').collect(); if parts.len() >= 3 { let version = parts[1].trim().to_string(); let suite = parts[2].trim().to_string(); version_suites.entry(version).or_default().push(suite); } } let mut versions: Vec = version_suites .into_iter() .map(|(version, suites)| VersionInfo { version, suites }) .collect(); versions.sort_by(|a, b| b.version.cmp(&a.version)); self.versions.insert(package.to_string(), versions); self.versions.get(package).map(|v| v.as_slice()) } _ => None, } } fn insert_package(&mut self, name: String, description: String) { let pos = self.packages.binary_search(&name).unwrap_or_else(|p| p); self.packages.insert(pos, name.clone()); self.descriptions.insert(name, description); } } /// Create a new shared cache backed by apt-cache. pub fn new_shared_cache() -> SharedPackageCache { Arc::new(RwLock::new(AptPackageCache::new())) } /// Stream package names and descriptions from `apt-cache search` into the /// shared cache. /// /// Each line is `name - description`, inserted as soon as it is read so /// completions are available immediately while the full list loads. pub async fn stream_packages_into(cache: &SharedPackageCache) { let Ok(mut child) = Command::new("apt-cache") .args(["search", "--names-only", "."]) .stdout(std::process::Stdio::piped()) .spawn() else { return; }; let Some(stdout) = child.stdout.take() else { return; }; let mut lines = BufReader::new(stdout).lines(); while let Ok(Some(line)) = lines.next_line().await { if let Some((name, description)) = line.split_once(" - ") { cache .write() .await .insert_package(name.to_string(), description.to_string()); } } } #[cfg(test)] /// Simple in-memory package cache for tests. #[derive(Default)] pub struct TestPackageCache { /// Packages and their optional descriptions. pub packages: Vec<(String, Option)>, /// Cached versions. pub versions: HashMap>, } #[cfg(test)] #[async_trait::async_trait] impl PackageCache for TestPackageCache { fn get_packages_with_prefix(&self, prefix: &str) -> Vec { let prefix_lower = prefix.to_ascii_lowercase(); let mut result: Vec = self .packages .iter() .filter(|(name, _)| name.starts_with(&prefix_lower)) .map(|(name, _)| name.clone()) .collect(); result.sort_unstable(); result } fn get_description(&self, package: &str) -> Option<&str> { self.packages .iter() .find(|(name, _)| name == package) .and_then(|(_, desc)| desc.as_deref()) } async fn load_versions(&mut self, package: &str) -> Option<&[VersionInfo]> { self.versions.get(package).map(|v| v.as_slice()) } fn insert_package(&mut self, name: String, description: String) { self.packages.push((name, Some(description))); } } #[cfg(test)] impl TestPackageCache { /// Create a shared test cache. pub fn new_shared(packages: &[(&str, Option<&str>)]) -> SharedPackageCache { let mut cache = TestPackageCache::default(); for &(name, desc) in packages { cache .packages .push((name.to_string(), desc.map(|s| s.to_string()))); } Arc::new(RwLock::new(cache)) } } #[cfg(test)] mod tests { use super::*; #[test] fn test_empty_cache() { let cache = AptPackageCache::new(); assert!(cache.get_packages_with_prefix("foo").is_empty()); } #[test] fn test_insert_package_sorted() { let mut cache = AptPackageCache::new(); cache.insert_package("cmake".to_string(), "cross-platform make".to_string()); cache.insert_package("apt".to_string(), "package manager".to_string()); cache.insert_package("zsh".to_string(), "shell".to_string()); cache.insert_package("debhelper".to_string(), "helper tools".to_string()); assert_eq!(cache.packages, vec!["apt", "cmake", "debhelper", "zsh"]); assert_eq!(cache.get_description("cmake"), Some("cross-platform make")); } #[test] fn test_test_cache_prefix() { let cache_arc = TestPackageCache::new_shared(&[ ("debhelper", None), ("debhelper-compat", None), ("cmake", Some("cross-platform make")), ]); let cache = cache_arc.try_read().unwrap(); let deb = cache.get_packages_with_prefix("deb"); assert_eq!(deb, vec!["debhelper", "debhelper-compat"]); let cm = cache.get_packages_with_prefix("cm"); assert_eq!(cm, vec!["cmake"]); assert_eq!(cache.get_description("cmake"), Some("cross-platform make")); assert_eq!(cache.get_description("debhelper"), None); } } debian-lsp-0.1.4/src/position.rs000064400000000000000000000072561046102023000146570ustar 00000000000000use text_size::{TextRange, TextSize}; use tower_lsp_server::ls_types::{Position, Range}; /// Convert TextSize to LSP Position pub fn offset_to_position(text: &str, offset: TextSize) -> Position { let mut line = 0; let mut line_start_offset = TextSize::from(0); for (i, ch) in text.char_indices() { let current_offset = TextSize::try_from(i).unwrap(); if current_offset >= offset { break; } if ch == '\n' { line += 1; line_start_offset = TextSize::try_from(i + 1).unwrap(); } } let character = (offset - line_start_offset).into(); Position { line, character } } /// Convert TextRange to LSP Range pub fn text_range_to_lsp_range(text: &str, range: TextRange) -> Range { Range { start: offset_to_position(text, range.start()), end: offset_to_position(text, range.end()), } } /// Convert LSP Position to TextSize pub fn try_position_to_offset(text: &str, position: Position) -> Option { let mut line = 0u32; let mut line_start = 0usize; // Find the byte offset where the requested line starts. for (i, ch) in text.char_indices() { if line == position.line { break; } if ch == '\n' { line += 1; line_start = i + 1; } } // If the requested line is beyond the available lines, return an error. if line < position.line { return None; } // If character is beyond the end of the target line, return an error. let line_end = text[line_start..] .find('\n') .map(|rel| line_start + rel) .unwrap_or(text.len()); let requested = line_start.checked_add(position.character as usize)?; if requested > line_end { return None; } TextSize::try_from(requested).ok() } /// Convert LSP Range to TextRange pub fn try_lsp_range_to_text_range(text: &str, range: &Range) -> Option { let start = try_position_to_offset(text, range.start)?; let end = try_position_to_offset(text, range.end)?; Some(TextRange::new(start, end)) } #[cfg(test)] mod tests { use super::*; #[test] fn test_try_position_to_offset_multiline_value_end() { let text = "Source: test\nSection: py\n"; let offset = try_position_to_offset(text, Position::new(1, 11)).unwrap(); let expected = TextSize::try_from(24usize).unwrap(); assert_eq!(offset, expected); } #[test] fn test_try_position_to_offset_returns_none_for_out_of_range_character() { let text = "Source: test\nSection: py\n"; let offset = try_position_to_offset(text, Position::new(1, 99)); assert!(offset.is_none()); } #[test] fn test_try_position_to_offset_returns_none_for_out_of_range_line() { let text = "Source: test\n"; let offset = try_position_to_offset(text, Position::new(5, 0)); assert!(offset.is_none()); } #[test] fn test_try_lsp_range_to_text_range_valid() { let text = "Source: test\nSection: py\n"; let range = Range::new(Position::new(1, 0), Position::new(1, 7)); let text_range = try_lsp_range_to_text_range(text, &range).unwrap(); let expected_start = TextSize::try_from(13usize).unwrap(); let expected_end = TextSize::try_from(20usize).unwrap(); assert_eq!(text_range.start(), expected_start); assert_eq!(text_range.end(), expected_end); } #[test] fn test_try_lsp_range_to_text_range_invalid_returns_none() { let text = "Source: test\n"; let range = Range::new(Position::new(10, 0), Position::new(10, 1)); assert!(try_lsp_range_to_text_range(text, &range).is_none()); } } debian-lsp-0.1.4/src/source_format/completion.rs000064400000000000000000000033251046102023000200250ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Position, Uri}; use super::detection::is_source_format_file; use super::fields::SOURCE_FORMATS; /// Get completion items for debian/source/format file pub fn get_completions(_uri: &Uri, _position: Position) -> Vec { if !is_source_format_file(_uri) { return Vec::new(); } SOURCE_FORMATS .iter() .map(|(format, description)| CompletionItem { label: (*format).to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some((*description).to_string()), insert_text: Some((*format).to_string()), ..Default::default() }) .collect() } #[cfg(test)] mod tests { use super::*; #[test] fn test_get_completions() { let uri = str::parse("file:///tmp/debian/source/format").unwrap(); let position = Position::new(0, 0); let completions = get_completions(&uri, position); assert_eq!(completions.len(), 5); assert!(completions .iter() .any(|c| c.label == "3.0 (quilt)" && c.kind == Some(CompletionItemKind::VALUE))); assert!(completions .iter() .any(|c| c.label == "3.0 (native)" && c.kind == Some(CompletionItemKind::VALUE))); assert!(completions .iter() .any(|c| c.label == "1.0" && c.kind == Some(CompletionItemKind::VALUE))); } #[test] fn test_get_completions_non_format_file() { let uri = str::parse("file:///tmp/debian/control").unwrap(); let position = Position::new(0, 0); let completions = get_completions(&uri, position); assert_eq!(completions.len(), 0); } } debian-lsp-0.1.4/src/source_format/detection.rs000064400000000000000000000014661046102023000176360ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if the given URI points to a debian/source/format file pub fn is_source_format_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/source/format") || path.ends_with("/debian/source/format") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_source_format_file() { assert!(is_source_format_file( &str::parse("file:///tmp/debian/source/format").unwrap() )); assert!(is_source_format_file( &str::parse("file:///tmp/source/format").unwrap() )); assert!(!is_source_format_file( &str::parse("file:///tmp/debian/control").unwrap() )); assert!(!is_source_format_file( &str::parse("file:///tmp/format").unwrap() )); } } debian-lsp-0.1.4/src/source_format/fields.rs000064400000000000000000000007011046102023000171150ustar 00000000000000/// Valid source format values for debian/source/format pub const SOURCE_FORMATS: &[(&str, &str)] = &[ ( "3.0 (quilt)", "Source format with quilt-based patches (recommended)", ), ( "3.0 (native)", "Source format for native Debian packages (no upstream)", ), ("1.0", "Legacy source format"), ("3.0 (git)", "Source format using git repository"), ("3.0 (custom)", "Custom source format"), ]; debian-lsp-0.1.4/src/source_format/mod.rs000064400000000000000000000001711046102023000164270ustar 00000000000000pub mod completion; pub mod detection; pub mod fields; pub use completion::*; pub use detection::is_source_format_file; debian-lsp-0.1.4/src/tests/completion.rs000064400000000000000000000013741046102023000163210ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, Position, Uri}; /// Get completion items for a debian/tests/control file pub fn get_completions(_uri: &Uri, _position: Position) -> Vec { // TODO: Implement completions for debian/tests/control // For now, return empty - we'll add field completions later // when we have a dedicated debian-tests crate vec![] } #[cfg(test)] mod tests { use super::*; #[test] fn test_get_completions_returns_empty_for_now() { let uri = "file:///debian/tests/control".parse().unwrap(); let position = Position::new(0, 0); let completions = get_completions(&uri, position); // For now, should return empty assert!(completions.is_empty()); } } debian-lsp-0.1.4/src/tests/detection.rs000064400000000000000000000025561046102023000161310ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a Debian tests/control file pub fn is_tests_control_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/debian/tests/control") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_tests_control_file() { let tests_control_paths = vec![ "file:///path/to/debian/tests/control", "file:///project/debian/tests/control", ]; let non_tests_control_paths = vec![ "file:///path/to/other.txt", "file:///path/to/debian/control", "file:///path/to/debian/copyright", "file:///path/to/debian/watch", "file:///path/to/tests/control", // Not in debian/ directory "file:///path/to/debian/tests/control.backup", ]; for path in tests_control_paths { let uri = path.parse::().unwrap(); assert!( is_tests_control_file(&uri), "Should detect tests/control file: {}", path ); } for path in non_tests_control_paths { let uri = path.parse::().unwrap(); assert!( !is_tests_control_file(&uri), "Should not detect as tests/control file: {}", path ); } } } debian-lsp-0.1.4/src/tests/mod.rs000064400000000000000000000006761046102023000147330ustar 00000000000000//! Module for handling debian/tests/control files //! //! For now, this provides basic file detection and empty completion support. //! In the future, this will be extended with a dedicated debian-tests crate //! for proper parsing and validation of autopkgtest control files. pub mod completion; pub mod detection; pub mod semantic; pub use completion::*; pub use detection::is_tests_control_file; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/tests/semantic.rs000064400000000000000000000055411046102023000157530ustar 00000000000000//! Semantic token generation for debian/tests/control files. use tower_lsp_server::ls_types::SemanticToken; use crate::deb822::semantic::{generate_tokens, FieldValidator}; /// Known field names for debian/tests/control (autopkgtest) const TESTS_CONTROL_FIELDS: &[&str] = &[ "Tests", "Test-Command", "Restrictions", "Features", "Depends", "Tests-Directory", "Classes", "Architecture", ]; /// Field validator for debian/tests/control files struct TestsControlFieldValidator; impl FieldValidator for TestsControlFieldValidator { fn get_standard_field_name(&self, name: &str) -> Option<&'static str> { let lower = name.to_lowercase(); TESTS_CONTROL_FIELDS .iter() .find(|f| f.to_lowercase() == lower) .copied() } } /// Generate semantic tokens for a debian/tests/control file pub fn generate_semantic_tokens(source_text: &str) -> Vec { let parsed = deb822_lossless::Deb822::parse(source_text); let deb822 = parsed.tree(); let validator = TestsControlFieldValidator; generate_tokens(&deb822, source_text, &validator) } #[cfg(test)] mod tests { use super::*; use crate::deb822::semantic::TokenType; #[test] fn test_known_fields() { let text = "Tests: my-test\nDepends: @\nRestrictions: needs-root\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens.len(), 6); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 5); // "Tests" assert_eq!(tokens[2].token_type, TokenType::Field as u32); assert_eq!(tokens[2].length, 7); // "Depends" assert_eq!(tokens[4].token_type, TokenType::Field as u32); assert_eq!(tokens[4].length, 12); // "Restrictions" } #[test] fn test_unknown_field() { let text = "Tests: my-test\nX-Custom: value\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens[2].token_type, TokenType::UnknownField as u32); assert_eq!(tokens[2].length, 8); // "X-Custom" } #[test] fn test_case_insensitive() { let text = "tests: my-test\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens[0].token_type, TokenType::Field as u32); } #[test] fn test_field_validator() { let validator = TestsControlFieldValidator; assert_eq!(validator.get_standard_field_name("Tests"), Some("Tests")); assert_eq!(validator.get_standard_field_name("tests"), Some("Tests")); assert_eq!( validator.get_standard_field_name("Test-Command"), Some("Test-Command") ); assert_eq!( validator.get_standard_field_name("Restrictions"), Some("Restrictions") ); assert_eq!(validator.get_standard_field_name("UnknownField"), None); } } debian-lsp-0.1.4/src/upstream_metadata/completion.rs000064400000000000000000000062221046102023000206540ustar 00000000000000use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind, Position}; use super::fields::UPSTREAM_FIELDS; /// Get completions for a debian/upstream/metadata file at the given position. /// /// If the cursor is at the start of a line (column 0), returns field name /// completions. Otherwise returns empty. pub fn get_completions(source_text: &str, position: Position) -> Vec { if position.character != 0 { return vec![]; } // Check if the cursor is on an empty line or at the start of a new line let lines: Vec<&str> = source_text.lines().collect(); let line = lines.get(position.line as usize).copied().unwrap_or(""); // Only offer field completions on empty or whitespace-only lines if !line.trim().is_empty() { return vec![]; } get_field_completions() } /// Generate field name completions for all known DEP-12 fields. fn get_field_completions() -> Vec { UPSTREAM_FIELDS .iter() .map(|field| CompletionItem { label: field.name.to_string(), kind: Some(CompletionItemKind::FIELD), detail: Some(field.description.to_string()), insert_text: Some(format!("{}: ", field.name)), ..Default::default() }) .collect() } #[cfg(test)] mod tests { use super::*; #[test] fn test_get_completions_at_start_of_empty_line() { let text = "Repository: https://example.com\n\n"; let completions = get_completions(text, Position::new(1, 0)); assert_eq!(completions.len(), UPSTREAM_FIELDS.len()); assert_eq!(completions[0].label, "Repository"); assert_eq!( completions[0].detail.as_deref(), Some("URL of the upstream source repository") ); } #[test] fn test_get_completions_not_at_column_zero() { let text = "Repository: https://example.com\n"; let completions = get_completions(text, Position::new(0, 12)); assert_eq!(completions.len(), 0); } #[test] fn test_get_completions_on_existing_field() { let text = "Repository: https://example.com\n"; let completions = get_completions(text, Position::new(0, 0)); assert_eq!(completions.len(), 0); } #[test] fn test_get_completions_empty_file() { let completions = get_completions("", Position::new(0, 0)); assert_eq!(completions.len(), UPSTREAM_FIELDS.len()); } #[test] fn test_get_completions_line_beyond_file() { let text = "Repository: https://example.com\n"; let completions = get_completions(text, Position::new(5, 0)); // Line 5 doesn't exist, defaults to empty string which is whitespace-only assert_eq!(completions.len(), UPSTREAM_FIELDS.len()); } #[test] fn test_field_completions_have_insert_text() { let completions = get_field_completions(); assert_eq!(completions.len(), UPSTREAM_FIELDS.len()); for c in &completions { assert_eq!(c.kind, Some(CompletionItemKind::FIELD)); assert!(c.insert_text.as_ref().unwrap().ends_with(": ")); assert!(c.detail.is_some()); } } } debian-lsp-0.1.4/src/upstream_metadata/detection.rs000064400000000000000000000024601046102023000204610ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a debian/upstream/metadata file. pub fn is_upstream_metadata_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/debian/upstream/metadata") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_upstream_metadata_file() { let valid_paths = vec![ "file:///path/to/debian/upstream/metadata", "file:///project/debian/upstream/metadata", ]; let invalid_paths = vec![ "file:///path/to/other.txt", "file:///path/to/debian/control", "file:///path/to/debian/copyright", "file:///path/to/upstream/metadata", "file:///path/to/debian/upstream/metadata.bak", ]; for path in valid_paths { let uri = path.parse::().unwrap(); assert!( is_upstream_metadata_file(&uri), "Should detect upstream/metadata file: {}", path ); } for path in invalid_paths { let uri = path.parse::().unwrap(); assert!( !is_upstream_metadata_file(&uri), "Should not detect as upstream/metadata file: {}", path ); } } } debian-lsp-0.1.4/src/upstream_metadata/fields.rs000064400000000000000000000073141046102023000177540ustar 00000000000000/// A field definition for the upstream/metadata file (DEP-12). pub struct UpstreamField { pub name: &'static str, pub description: &'static str, } impl UpstreamField { pub const fn new(name: &'static str, description: &'static str) -> Self { Self { name, description } } } // TODO: Read field definitions from the debian-json-schemas project instead of hardcoding. /// DEP-12 upstream metadata fields. pub const UPSTREAM_FIELDS: &[UpstreamField] = &[ UpstreamField::new("Repository", "URL of the upstream source repository"), UpstreamField::new( "Repository-Browse", "Web interface for the upstream repository", ), UpstreamField::new("Bug-Database", "URL of the upstream bug tracking system"), UpstreamField::new("Bug-Submit", "URL for submitting new upstream bugs"), UpstreamField::new("Name", "Human-readable name of the upstream project"), UpstreamField::new("Contact", "Contact information for the upstream authors"), UpstreamField::new("Changelog", "URL of the upstream changelog"), UpstreamField::new("Documentation", "URL of the upstream documentation"), UpstreamField::new("FAQ", "URL of the upstream FAQ"), UpstreamField::new("Donation", "URL for donating to the upstream project"), UpstreamField::new("Screenshots", "URL of upstream screenshots"), UpstreamField::new("Gallery", "URL of an upstream image gallery"), UpstreamField::new("Webservice", "URL of the upstream web service"), UpstreamField::new("Security-Contact", "Contact for reporting security issues"), UpstreamField::new("CPE", "Common Platform Enumeration identifier"), UpstreamField::new("ASCL-Id", "Astrophysics Source Code Library identifier"), UpstreamField::new("Cite-As", "Preferred citation for the software"), UpstreamField::new("Funding", "Funding information for the project"), UpstreamField::new("Reference", "Bibliographic references for the software"), UpstreamField::new("Registry", "External software registry entries"), UpstreamField::new( "Other-References", "Additional references not covered by Reference", ), ]; /// Look up the standard (canonical) casing for a field name. pub fn get_standard_field_name(field_name: &str) -> Option<&'static str> { let lowercase = field_name.to_lowercase(); UPSTREAM_FIELDS .iter() .find(|f| f.name.to_lowercase() == lowercase) .map(|f| f.name) } #[cfg(test)] mod tests { use super::*; #[test] fn test_upstream_fields() { assert_eq!(UPSTREAM_FIELDS.len(), 21); let names: Vec<_> = UPSTREAM_FIELDS.iter().map(|f| f.name).collect(); assert_eq!(names[0], "Repository"); assert_eq!(names[1], "Repository-Browse"); assert_eq!(names[2], "Bug-Database"); assert_eq!(names[3], "Bug-Submit"); assert_eq!(names[4], "Name"); } #[test] fn test_upstream_field_validity() { for field in UPSTREAM_FIELDS { assert!(!field.name.is_empty(), "Field name must not be empty"); assert!( !field.description.is_empty(), "Description for {} must not be empty", field.name ); } } #[test] fn test_get_standard_field_name() { assert_eq!(get_standard_field_name("Repository"), Some("Repository")); assert_eq!(get_standard_field_name("repository"), Some("Repository")); assert_eq!( get_standard_field_name("bug-database"), Some("Bug-Database") ); assert_eq!(get_standard_field_name("CPE"), Some("CPE")); assert_eq!(get_standard_field_name("cpe"), Some("CPE")); assert_eq!(get_standard_field_name("UnknownField"), None); } } debian-lsp-0.1.4/src/upstream_metadata/mod.rs000064400000000000000000000005651046102023000172660ustar 00000000000000//! Module for handling debian/upstream/metadata files (DEP-12). //! //! These files use YAML format and contain machine-readable metadata //! about the upstream project. pub mod completion; pub mod detection; pub mod fields; pub mod semantic; pub use completion::get_completions; pub use detection::is_upstream_metadata_file; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/upstream_metadata/semantic.rs000064400000000000000000000123551046102023000203120ustar 00000000000000//! Semantic token generation for debian/upstream/metadata files. use tower_lsp_server::ls_types::SemanticToken; use yaml_edit::{Document, YamlNode}; use super::fields::get_standard_field_name; use crate::deb822::semantic::{SemanticTokensBuilder, TokenType}; /// Generate semantic tokens for a debian/upstream/metadata file. pub fn generate_semantic_tokens(source_text: &str) -> Vec { let doc = match source_text.parse::() { Ok(doc) => doc, Err(_) => return vec![], }; let mapping = match doc.as_mapping() { Some(m) => m, None => return vec![], }; let mut builder = SemanticTokensBuilder::new(); for entry in mapping.entries() { // Emit token for the key if let Some(YamlNode::Scalar(key_scalar)) = entry.key_node() { let key_text = key_scalar.as_string(); let pos = key_scalar.start_position(source_text); let range = key_scalar.byte_range(); let len = range.end - range.start; // LineColumn is 1-indexed, LSP is 0-indexed let line = pos.line.saturating_sub(1) as u32; let col = pos.column.saturating_sub(1) as u32; let token_type = if get_standard_field_name(&key_text).is_some() { TokenType::Field } else { TokenType::UnknownField }; builder.push( line, col, len, token_type, crate::deb822::semantic::token_modifier::DECLARATION, ); } // Emit token for the value (only for scalar values) if let Some(YamlNode::Scalar(val_scalar)) = entry.value_node() { let pos = val_scalar.start_position(source_text); let range = val_scalar.byte_range(); let len = range.end - range.start; let line = pos.line.saturating_sub(1) as u32; let col = pos.column.saturating_sub(1) as u32; if len > 0 { builder.push(line, col, len, TokenType::Value, 0); } } } builder.build() } #[cfg(test)] mod tests { use super::*; #[test] fn test_known_fields() { let text = "Repository: https://github.com/example/project\nBug-Database: https://github.com/example/project/issues\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens.len(), 4); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 10); // "Repository" assert_eq!(tokens[1].token_type, TokenType::Value as u32); assert_eq!(tokens[2].token_type, TokenType::Field as u32); assert_eq!(tokens[2].length, 12); // "Bug-Database" assert_eq!(tokens[3].token_type, TokenType::Value as u32); } #[test] fn test_unknown_field() { let text = "Repository: https://example.com\nX-Custom: value\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens.len(), 4); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[2].token_type, TokenType::UnknownField as u32); assert_eq!(tokens[2].length, 8); // "X-Custom" } #[test] fn test_empty_text() { let tokens = generate_semantic_tokens(""); assert_eq!(tokens.len(), 0); } #[test] fn test_invalid_yaml() { let tokens = generate_semantic_tokens("{{invalid yaml"); assert_eq!(tokens.len(), 0); } #[test] fn test_non_mapping_document() { // A YAML document that is a sequence, not a mapping let tokens = generate_semantic_tokens("- item1\n- item2\n"); assert_eq!(tokens.len(), 0); } #[test] fn test_sequence_value_skipped() { // Registry has a sequence value — only the key should get a token let text = "Registry:\n - Name: PyPI\n Entry: example\n"; let tokens = generate_semantic_tokens(text); // Only the top-level key "Registry" gets a token (value is a sequence, not scalar) assert_eq!(tokens.len(), 1); assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 8); // "Registry" } #[test] fn test_declaration_modifier_on_keys() { let text = "Repository: https://example.com\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens.len(), 2); // Key should have DECLARATION modifier assert_eq!( tokens[0].token_modifiers_bitset, crate::deb822::semantic::token_modifier::DECLARATION ); // Value should have no modifiers assert_eq!(tokens[1].token_modifiers_bitset, 0); } #[test] fn test_delta_positions() { let text = "Repository: https://example.com\nBug-Database: https://bugs.example.com\n"; let tokens = generate_semantic_tokens(text); assert_eq!(tokens.len(), 4); // First key at line 0 assert_eq!(tokens[0].delta_line, 0); assert_eq!(tokens[0].delta_start, 0); // First value on same line assert_eq!(tokens[1].delta_line, 0); // Second key on next line assert_eq!(tokens[2].delta_line, 1); assert_eq!(tokens[2].delta_start, 0); } } debian-lsp-0.1.4/src/watch/completion.rs000064400000000000000000000360251046102023000162660ustar 00000000000000use crate::deb822::completion::FieldInfo; use crate::position::try_position_to_offset; use tower_lsp_server::ls_types::{ CompletionItem, CompletionItemKind, Documentation, Position, Uri, }; use super::detection::is_watch_file; use super::fields::{OptionValueType, WATCH_FIELDS, WATCH_LINEBASED_VERSIONS, WATCH_VERSIONS}; /// Build a `FieldInfo` slice for deb822 (v5) field name completions. fn deb822_field_infos() -> Vec { WATCH_FIELDS .iter() .map(|f| FieldInfo::new(f.deb822_name, f.description)) .collect() } /// Get completion items for a v1-4 (line-based) watch file using the CST. pub fn get_linebased_completions( uri: &Uri, wf: &debian_watch::linebased::WatchFile, source_text: &str, position: Position, ) -> Vec { if !is_watch_file(uri) { return Vec::new(); } let Some(offset) = try_position_to_offset(source_text, position) else { return Vec::new(); }; // Walk ancestors of the token at the cursor to determine context if let Some(token) = wf.syntax().token_at_offset(offset).right_biased() { for ancestor in token.parent_ancestors() { match ancestor.kind() { debian_watch::SyntaxKind::OPTION => { // If cursor is after '=', offer value completions let has_eq_before_cursor = ancestor.children_with_tokens().any(|el| { el.kind() == debian_watch::SyntaxKind::EQUALS && el.text_range().end() <= offset }); if has_eq_before_cursor { let key = ancestor.children_with_tokens().find_map(|el| { if el.kind() == debian_watch::SyntaxKind::KEY { Some(el.as_token()?.text().to_string()) } else { None } }); let prefix = if token.kind() == debian_watch::SyntaxKind::VALUE { token.text() } else { "" }; return key .and_then(|k| { WATCH_FIELDS .iter() .find(|f| f.linebased_name == Some(k.as_str())) }) .map(|f| (f.complete_values)(prefix)) .unwrap_or_default(); } // Cursor is on the key name let prefix = if token.kind() == debian_watch::SyntaxKind::KEY { token.text() } else { "" }; return get_linebased_option_completions_with_prefix(prefix); } debian_watch::SyntaxKind::OPTS_LIST => { // Cursor is in opts area but not inside an OPTION node return get_linebased_option_completions_with_prefix(""); } debian_watch::SyntaxKind::VERSION => { // If cursor is after '=', offer version number completions let has_eq_before_cursor = ancestor.children_with_tokens().any(|el| { el.kind() == debian_watch::SyntaxKind::EQUALS && el.text_range().end() <= offset }); if has_eq_before_cursor { return get_linebased_version_value_completions(); } } _ => {} } } } // Default: offer version and option completions let mut completions = Vec::new(); completions.extend(get_linebased_option_completions()); completions.extend(get_linebased_version_completions()); completions } /// Get option name completions filtered by prefix for v1-4 watch files. fn get_linebased_option_completions_with_prefix(prefix: &str) -> Vec { let normalized = prefix.trim().to_ascii_lowercase(); WATCH_FIELDS .iter() .filter_map(|field| { let name = field.linebased_name?; if !name.starts_with(&normalized) { return None; } let insert_text = match field.value_type { OptionValueType::Boolean => name.to_string(), OptionValueType::String | OptionValueType::Enum(_) => { format!("{}=", name) } }; Some(CompletionItem { label: name.to_string(), kind: Some(CompletionItemKind::PROPERTY), detail: Some(field.description.to_string()), documentation: Some(Documentation::String(field.description.to_string())), insert_text: Some(insert_text), ..Default::default() }) }) .collect() } /// Get completion items for a v5 (deb822) watch file, using position-aware completions. pub fn get_completions_deb822( deb822: &deb822_lossless::Deb822, source_text: &str, position: Position, ) -> Vec { let field_infos = deb822_field_infos(); crate::deb822::completion::get_completions( deb822, source_text, position, &field_infos, |field_name, prefix| { let lower = field_name.to_lowercase(); WATCH_FIELDS .iter() .find(|f| f.deb822_name.to_lowercase() == lower) .map(|f| (f.complete_values)(prefix)) .unwrap_or_default() }, ) } /// Get completion items for v1-4 watch file options (line-based format). pub fn get_linebased_option_completions() -> Vec { WATCH_FIELDS .iter() .filter_map(|field| { let name = field.linebased_name?; let insert_text = match field.value_type { OptionValueType::Boolean => name.to_string(), OptionValueType::String | OptionValueType::Enum(_) => { format!("{}=", name) } }; Some(CompletionItem { label: name.to_string(), kind: Some(CompletionItemKind::PROPERTY), detail: Some(field.description.to_string()), documentation: Some(Documentation::String(field.description.to_string())), insert_text: Some(insert_text), ..Default::default() }) }) .collect() } /// Get completion items for watch file `version=N` lines (used in default context). pub fn get_linebased_version_completions() -> Vec { WATCH_VERSIONS .iter() .map(|version| CompletionItem { label: format!("version={}", version), kind: Some(CompletionItemKind::KEYWORD), detail: Some(format!("Watch file format version {}", version)), insert_text: Some(format!("version={}", version)), ..Default::default() }) .collect() } /// Get completion items for version numbers (used when cursor is after `version=`). fn get_linebased_version_value_completions() -> Vec { WATCH_LINEBASED_VERSIONS .iter() .map(|version| CompletionItem { label: version.to_string(), kind: Some(CompletionItemKind::VALUE), detail: Some(format!("Watch file format version {}", version)), ..Default::default() }) .collect() } #[cfg(test)] mod tests { use super::*; fn parse_linebased(text: &str) -> debian_watch::linebased::WatchFile { debian_watch::linebased::parse_watch_file(text).tree() } #[test] fn test_get_linebased_completions_for_watch_file() { let uri: Uri = str::parse("file:///path/to/debian/watch").unwrap(); let text = "version=4\n"; let wf = parse_linebased(text); let completions = get_linebased_completions(&uri, &wf, text, Position::new(0, 0)); assert!(!completions.is_empty()); let option_count = completions .iter() .filter(|c| c.kind == Some(CompletionItemKind::PROPERTY)) .count(); let version_count = completions .iter() .filter(|c| c.kind == Some(CompletionItemKind::KEYWORD)) .count(); assert!(option_count > 0); assert!(version_count > 0); } #[test] fn test_get_linebased_completions_for_non_watch_file() { let uri: Uri = str::parse("file:///path/to/other.txt").unwrap(); let text = "version=4\n"; let wf = parse_linebased(text); let completions = get_linebased_completions(&uri, &wf, text, Position::new(0, 0)); assert!(completions.is_empty()); } #[test] fn test_get_linebased_completions_in_opts_option_name() { let uri: Uri = str::parse("file:///path/to/debian/watch").unwrap(); let text = "version=4\nopts=\"mode=git,\" https://example.com\n"; let wf = parse_linebased(text); // Cursor right after the comma — should offer option names let completions = get_linebased_completions(&uri, &wf, text, Position::new(1, 14)); assert!(!completions.is_empty()); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"pgpmode")); } #[test] fn test_get_linebased_completions_in_opts_option_value() { let uri: Uri = str::parse("file:///path/to/debian/watch").unwrap(); let text = "version=4\nopts=\"mode=git\" https://example.com\n"; let wf = parse_linebased(text); // Cursor on the value "git" — should offer mode values let completions = get_linebased_completions(&uri, &wf, text, Position::new(1, 13)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"git")); assert!(labels.contains(&"lwp")); assert!(labels.contains(&"svn")); } #[test] fn test_get_linebased_completions_version_value() { let uri: Uri = str::parse("file:///path/to/debian/watch").unwrap(); let text = "version=4\n"; let wf = parse_linebased(text); // Cursor on the version number after '=' let completions = get_linebased_completions(&uri, &wf, text, Position::new(0, 9)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["1", "2", "3", "4"]); assert!(completions .iter() .all(|c| c.kind == Some(CompletionItemKind::VALUE))); } #[test] fn test_get_completions_deb822_on_field_key() { let text = "Version: 5\n\nSource: https://example.com\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions_deb822(&deb822, text, Position::new(2, 3)); let field_count = completions .iter() .filter(|c| c.kind == Some(CompletionItemKind::FIELD)) .count(); assert!(field_count > 0); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert!(labels.contains(&"Source")); assert!(labels.contains(&"Matching-Pattern")); assert!(labels.contains(&"Version")); } #[test] fn test_get_completions_deb822_on_string_value() { let text = "Version: 5\n\nSource: https://example.com\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); // Source is a string field, no value completions let completions = get_completions_deb822(&deb822, text, Position::new(2, 15)); assert!(completions.is_empty()); } #[test] fn test_get_completions_deb822_on_boolean_value() { let text = "Version: 5\n\nBare: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions_deb822(&deb822, text, Position::new(2, 6)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["yes", "no"]); } #[test] fn test_get_completions_deb822_on_enum_value() { let text = "Version: 5\n\nMode: \n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions_deb822(&deb822, text, Position::new(2, 6)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["lwp", "git", "svn"]); } #[test] fn test_get_completions_deb822_on_enum_value_with_prefix() { let text = "Version: 5\n\nMode: g\n"; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions_deb822(&deb822, text, Position::new(2, 7)); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); assert_eq!(labels, vec!["git"]); } #[test] fn test_get_completions_deb822_on_empty() { let text = ""; let deb822 = deb822_lossless::Deb822::parse(text).to_result().unwrap(); let completions = get_completions_deb822(&deb822, text, Position::new(0, 0)); let field_count = completions .iter() .filter(|c| c.kind == Some(CompletionItemKind::FIELD)) .count(); assert!(field_count > 0); } #[test] fn test_option_completions() { let completions = get_linebased_option_completions(); assert!(!completions.is_empty()); for completion in &completions { assert!(!completion.label.is_empty()); assert_eq!(completion.kind, Some(CompletionItemKind::PROPERTY)); assert!(completion.detail.is_some()); assert!(completion.documentation.is_some()); assert!(completion.insert_text.is_some()); } let labels: Vec<_> = completions.iter().map(|c| &c.label).collect(); assert!(labels.iter().any(|l| *l == "mode")); assert!(labels.iter().any(|l| *l == "pgpmode")); assert!(labels.iter().any(|l| *l == "uversionmangle")); } #[test] fn test_option_completions_exclude_v5_only() { let completions = get_linebased_option_completions(); let labels: Vec<_> = completions.iter().map(|c| c.label.as_str()).collect(); // v5-only fields should not appear as line-based options assert!(!labels.contains(&"Source")); assert!(!labels.contains(&"Matching-Pattern")); assert!(!labels.contains(&"Version")); } #[test] fn test_version_completions() { let completions = get_linebased_version_completions(); assert_eq!(completions.len(), WATCH_VERSIONS.len()); for completion in &completions { assert!(!completion.label.is_empty()); assert_eq!(completion.kind, Some(CompletionItemKind::KEYWORD)); assert!(completion.label.starts_with("version=")); } } } debian-lsp-0.1.4/src/watch/detection.rs000064400000000000000000000023631046102023000160710ustar 00000000000000use tower_lsp_server::ls_types::Uri; /// Check if a given URL represents a Debian watch file pub fn is_watch_file(uri: &Uri) -> bool { let path = uri.as_str(); path.ends_with("/watch") || path.ends_with("/debian/watch") } #[cfg(test)] mod tests { use super::*; #[test] fn test_is_watch_file() { let watch_paths = vec![ "file:///path/to/debian/watch", "file:///project/debian/watch", "file:///watch", "file:///some/path/watch", ]; let non_watch_paths = vec![ "file:///path/to/other.txt", "file:///path/to/watch.txt", "file:///path/to/mywatch", "file:///path/to/debian/watch.backup", "file:///path/to/debian/control", "file:///path/to/debian/copyright", ]; for path in watch_paths { let uri = path.parse::().unwrap(); assert!(is_watch_file(&uri), "Should detect watch file: {}", path); } for path in non_watch_paths { let uri = path.parse::().unwrap(); assert!( !is_watch_file(&uri), "Should not detect as watch file: {}", path ); } } } debian-lsp-0.1.4/src/watch/fields.rs000064400000000000000000000274231046102023000153650ustar 00000000000000/// Type of value a watch field/option accepts #[derive(Debug, Clone, Copy, PartialEq, Eq)] pub enum OptionValueType { /// Boolean option (no value) Boolean, /// String value String, /// Enum with predefined values Enum(&'static [&'static str]), } use tower_lsp_server::ls_types::{CompletionItem, CompletionItemKind}; /// A watch file field definition, used for both v1-4 line-based options and v5 deb822 fields. pub struct WatchField { /// Canonical field name (deb822 / title-case form, used in v5). pub deb822_name: &'static str, /// Option name used in v1-4 line-based format, or `None` for v5-only fields. pub linebased_name: Option<&'static str>, /// Human-readable description. pub description: &'static str, /// Type of value the field accepts. pub value_type: OptionValueType, /// Callback returning completion items for this field's values. /// Receives the prefix already typed by the user for filtering. pub complete_values: fn(&str) -> Vec, } fn no_completions(_prefix: &str) -> Vec { vec![] } fn enum_completions(values: &[&str], prefix: &str) -> Vec { let normalized = prefix.trim().to_ascii_lowercase(); values .iter() .filter(|v| v.starts_with(&normalized)) .map(|&v| CompletionItem { label: v.to_string(), kind: Some(CompletionItemKind::VALUE), ..Default::default() }) .collect() } fn boolean_completions(prefix: &str) -> Vec { enum_completions(&["yes", "no"], prefix) } fn compression_completions(prefix: &str) -> Vec { enum_completions(&["gzip", "xz", "bzip2", "lzma", "default"], prefix) } fn mode_completions(prefix: &str) -> Vec { enum_completions(&["lwp", "git", "svn"], prefix) } fn pgpmode_completions(prefix: &str) -> Vec { enum_completions( &[ "auto", "default", "mangle", "next", "previous", "self", "gittag", ], prefix, ) } fn searchmode_completions(prefix: &str) -> Vec { enum_completions(&["html", "plain"], prefix) } fn gitmode_completions(prefix: &str) -> Vec { enum_completions(&["shallow", "full"], prefix) } fn gitexport_completions(prefix: &str) -> Vec { enum_completions(&["default", "all"], prefix) } fn ctype_completions(prefix: &str) -> Vec { enum_completions(&["perl", "nodejs"], prefix) } impl WatchField { pub const fn new( deb822_name: &'static str, linebased_name: Option<&'static str>, description: &'static str, value_type: OptionValueType, complete_values: fn(&str) -> Vec, ) -> Self { Self { deb822_name, linebased_name, description, value_type, complete_values, } } } /// All known watch file fields/options. pub const WATCH_FIELDS: &[WatchField] = &[ // v5-only fields (no line-based equivalent) WatchField::new( "Version", None, "Watch file format version", OptionValueType::String, no_completions, ), WatchField::new( "Source", None, "URL to check for upstream releases", OptionValueType::String, no_completions, ), WatchField::new( "Matching-Pattern", None, "Regex pattern to match upstream files", OptionValueType::String, no_completions, ), WatchField::new( "Template", None, "URL template for constructing download URLs", OptionValueType::String, no_completions, ), WatchField::new( "Owner", None, "Owner name for repository-based sources", OptionValueType::String, no_completions, ), WatchField::new( "Project", None, "Project name for repository-based sources", OptionValueType::String, no_completions, ), // Fields available in both v1-4 (as options) and v5 (as deb822 fields) WatchField::new( "Component", Some("component"), "Component name for multi-tarball packages", OptionValueType::String, no_completions, ), WatchField::new( "Compression", Some("compression"), "Compression format (gzip, xz, bzip2, lzma)", OptionValueType::Enum(&["gzip", "xz", "bzip2", "lzma", "default"]), compression_completions, ), WatchField::new( "Mode", Some("mode"), "Download mode (lwp, git, svn)", OptionValueType::Enum(&["lwp", "git", "svn"]), mode_completions, ), WatchField::new( "Pgpmode", Some("pgpmode"), "PGP verification mode", OptionValueType::Enum(&[ "auto", "default", "mangle", "next", "previous", "self", "gittag", ]), pgpmode_completions, ), WatchField::new( "Searchmode", Some("searchmode"), "Search mode for finding upstream versions", OptionValueType::Enum(&["html", "plain"]), searchmode_completions, ), WatchField::new( "Gitmode", Some("gitmode"), "Git clone mode", OptionValueType::Enum(&["shallow", "full"]), gitmode_completions, ), WatchField::new( "Gitexport", Some("gitexport"), "Git export mode", OptionValueType::Enum(&["default", "all"]), gitexport_completions, ), WatchField::new( "Pretty", Some("pretty"), "Pretty format for git tags", OptionValueType::String, no_completions, ), WatchField::new( "Uversionmangle", Some("uversionmangle"), "Upstream version mangling rules (s/pattern/replacement/)", OptionValueType::String, no_completions, ), WatchField::new( "Oversionmangle", Some("oversionmangle"), "Upstream version mangling rules (alternative name)", OptionValueType::String, no_completions, ), WatchField::new( "Dversionmangle", Some("dversionmangle"), "Debian version mangling rules (s/pattern/replacement/)", OptionValueType::String, no_completions, ), WatchField::new( "Dirversionmangle", Some("dirversionmangle"), "Directory version mangling rules for mode=git", OptionValueType::String, no_completions, ), WatchField::new( "Pagemangle", Some("pagemangle"), "Page content mangling rules", OptionValueType::String, no_completions, ), WatchField::new( "Downloadurlmangle", Some("downloadurlmangle"), "Download URL mangling rules", OptionValueType::String, no_completions, ), WatchField::new( "Pgpsigurlmangle", Some("pgpsigurlmangle"), "PGP signature URL mangling rules", OptionValueType::String, no_completions, ), WatchField::new( "Filenamemangle", Some("filenamemangle"), "Filename mangling rules", OptionValueType::String, no_completions, ), WatchField::new( "Versionmangle", Some("versionmangle"), "Version policy (debian, same, previous, ignore, group, checksum)", OptionValueType::String, no_completions, ), WatchField::new( "User-Agent", Some("user-agent"), "User agent string for HTTP requests", OptionValueType::String, no_completions, ), WatchField::new( "Useragent", Some("useragent"), "User agent string for HTTP requests (alternative name)", OptionValueType::String, no_completions, ), WatchField::new( "Ctype", Some("ctype"), "Component type (perl, nodejs)", OptionValueType::Enum(&["perl", "nodejs"]), ctype_completions, ), WatchField::new( "Repacksuffix", Some("repacksuffix"), "Suffix for repacked tarballs", OptionValueType::String, no_completions, ), WatchField::new( "Decompress", Some("decompress"), "Decompress downloaded files", OptionValueType::Boolean, boolean_completions, ), WatchField::new( "Bare", Some("bare"), "Use bare git clone for mode=git", OptionValueType::Boolean, boolean_completions, ), WatchField::new( "Repack", Some("repack"), "Repack the upstream tarball", OptionValueType::Boolean, boolean_completions, ), ]; /// Get the standard (canonical deb822) name for a watch field. pub fn get_standard_field_name(field_name: &str) -> Option<&'static str> { let lower = field_name.to_lowercase(); WATCH_FIELDS .iter() .find(|f| f.deb822_name.to_lowercase() == lower) .map(|f| f.deb822_name) } /// Watch file format versions pub const WATCH_VERSIONS: &[u32] = &[1, 2, 3, 4, 5]; /// Line-based watch file format versions (v5 uses deb822 format) pub const WATCH_LINEBASED_VERSIONS: &[u32] = &[1, 2, 3, 4]; #[cfg(test)] mod tests { use super::*; #[test] fn test_watch_fields() { assert!(!WATCH_FIELDS.is_empty()); assert!(WATCH_FIELDS.len() >= 20); let deb822_names: Vec<_> = WATCH_FIELDS.iter().map(|f| f.deb822_name).collect(); assert!(deb822_names.contains(&"Mode")); assert!(deb822_names.contains(&"Pgpmode")); assert!(deb822_names.contains(&"Uversionmangle")); assert!(deb822_names.contains(&"Compression")); assert!(deb822_names.contains(&"Source")); assert!(deb822_names.contains(&"Matching-Pattern")); assert!(deb822_names.contains(&"Version")); } #[test] fn test_linebased_options() { let options: Vec<_> = WATCH_FIELDS .iter() .filter_map(|f| f.linebased_name) .collect(); assert!(options.len() >= 20); assert!(options.contains(&"mode")); assert!(options.contains(&"pgpmode")); assert!(options.contains(&"uversionmangle")); assert!(options.contains(&"compression")); } #[test] fn test_v5_only_fields_have_no_linebased_name() { for field in WATCH_FIELDS { if [ "Version", "Source", "Matching-Pattern", "Template", "Owner", "Project", ] .contains(&field.deb822_name) { assert!( field.linebased_name.is_none(), "{} should be v5-only", field.deb822_name ); } } } #[test] fn test_watch_field_validity() { for field in WATCH_FIELDS { assert!(!field.deb822_name.is_empty()); assert!(!field.description.is_empty()); if let OptionValueType::Enum(values) = field.value_type { assert!( !values.is_empty(), "Enum field {} has no values", field.deb822_name ); } } } #[test] fn test_get_standard_field_name() { assert_eq!(get_standard_field_name("Source"), Some("Source")); assert_eq!(get_standard_field_name("source"), Some("Source")); assert_eq!( get_standard_field_name("Matching-Pattern"), Some("Matching-Pattern") ); assert_eq!(get_standard_field_name("mode"), Some("Mode")); assert_eq!(get_standard_field_name("UnknownField"), None); } #[test] fn test_watch_versions() { assert_eq!(WATCH_VERSIONS, &[1, 2, 3, 4, 5]); } } debian-lsp-0.1.4/src/watch/mod.rs000064400000000000000000000002571046102023000146720ustar 00000000000000pub mod completion; pub mod detection; pub mod fields; pub mod semantic; pub use completion::*; pub use detection::is_watch_file; pub use semantic::generate_semantic_tokens; debian-lsp-0.1.4/src/watch/semantic.rs000064400000000000000000000140311046102023000157110ustar 00000000000000//! Semantic token generation for Debian watch files. //! //! Supports both deb822 (v5) and line-based (v1-4) watch file formats. use tower_lsp_server::ls_types::SemanticToken; use crate::deb822::semantic::{SemanticTokensBuilder, TokenType}; use crate::position::offset_to_position; /// Field validator for v5 watch files struct WatchFieldValidator; impl crate::deb822::semantic::FieldValidator for WatchFieldValidator { fn get_standard_field_name(&self, name: &str) -> Option<&'static str> { super::fields::get_standard_field_name(name) } } /// Generate semantic tokens for a watch file pub fn generate_semantic_tokens( parse: &debian_watch::parse::Parse, source_text: &str, ) -> Vec { match parse.to_watch_file() { debian_watch::parse::ParsedWatchFile::Deb822(_) => generate_deb822_tokens(source_text), debian_watch::parse::ParsedWatchFile::LineBased(_) => { generate_linebased_tokens(source_text) } } } /// Generate tokens for v5 deb822 watch files fn generate_deb822_tokens(source_text: &str) -> Vec { let deb822_parse = deb822_lossless::Deb822::parse(source_text); let deb822 = deb822_parse.tree(); let validator = WatchFieldValidator; crate::deb822::semantic::generate_tokens(&deb822, source_text, &validator) } /// Generate tokens for v1-4 line-based watch files fn generate_linebased_tokens(source_text: &str) -> Vec { use debian_watch::SyntaxKind; let parsed = debian_watch::linebased::parse_watch_file(source_text); let wf = parsed.tree(); let mut builder = SemanticTokensBuilder::new(); for element in wf.syntax().descendants_with_tokens() { if let rowan::NodeOrToken::Token(token) = element { let kind = token.kind(); let token_type = match kind { SyntaxKind::KEY => Some(TokenType::Field), SyntaxKind::VALUE => { let parent_kind = token.parent().map(|p| p.kind()); match parent_kind { Some(SyntaxKind::VERSION) | Some(SyntaxKind::OPTION) | Some(SyntaxKind::URL) | Some(SyntaxKind::MATCHING_PATTERN) | Some(SyntaxKind::VERSION_POLICY) | Some(SyntaxKind::SCRIPT) => Some(TokenType::Value), _ => None, } } SyntaxKind::COMMENT => Some(TokenType::Comment), _ => None, }; if let Some(tt) = token_type { let range = token.text_range(); let start_pos = offset_to_position(source_text, range.start()); let length = (usize::from(range.end()) - usize::from(range.start())) as u32; if length > 0 { builder.push(start_pos.line, start_pos.character, length, tt, 0); } } } } builder.build() } #[cfg(test)] mod tests { use super::*; #[test] fn test_v5_known_fields() { let text = "Version: 5\n\nSource: https://github.com/owner/repo/tags\nMatching-Pattern: .*/v?(\\d[\\d.]*)/.tar.gz\n"; let parsed = debian_watch::parse::Parse::parse(text); let tokens = generate_semantic_tokens(&parsed, text); assert!(!tokens.is_empty()); // "Version" is a known field assert_eq!(tokens[0].token_type, TokenType::Field as u32); assert_eq!(tokens[0].length, 7); } #[test] fn test_v5_unknown_field() { let text = "Version: 5\n\nSource: https://example.com\nX-Custom: value\n"; let parsed = debian_watch::parse::Parse::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let unknown = tokens .iter() .find(|t| t.token_type == TokenType::UnknownField as u32); assert!(unknown.is_some(), "Should have an unknown field token"); } #[test] fn test_v4_produces_tokens() { let text = "version=4\nhttps://example.com/files .*/foo-(\\d[\\d.]*)/.tar\\.gz\n"; let parsed = debian_watch::parse::Parse::parse(text); let tokens = generate_semantic_tokens(&parsed, text); assert!(!tokens.is_empty(), "v4 watch files should produce tokens"); // Should have a field token for "version" let has_field = tokens .iter() .any(|t| t.token_type == TokenType::Field as u32); assert!(has_field, "Should have a field token"); } #[test] fn test_v4_comment() { let text = "version=4\n# This is a comment\nhttps://example.com .*/foo-(\\d[\\d.]*)/.tar\\.gz\n"; let parsed = debian_watch::parse::Parse::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let has_comment = tokens .iter() .any(|t| t.token_type == TokenType::Comment as u32); assert!(has_comment, "Should have a comment token"); } #[test] fn test_v4_url_and_pattern() { let text = "version=4\nhttps://example.com/files .*/foo-(\\d[\\d.]*)/.tar\\.gz\n"; let parsed = debian_watch::parse::Parse::parse(text); let tokens = generate_semantic_tokens(&parsed, text); let value_tokens: Vec<_> = tokens .iter() .filter(|t| t.token_type == TokenType::Value as u32) .collect(); assert!( value_tokens.len() >= 2, "Should have value tokens for version number, URL and/or pattern" ); } #[test] fn test_field_validator() { let validator = WatchFieldValidator; use crate::deb822::semantic::FieldValidator; assert_eq!(validator.get_standard_field_name("Source"), Some("Source")); assert_eq!(validator.get_standard_field_name("source"), Some("Source")); assert_eq!( validator.get_standard_field_name("Matching-Pattern"), Some("Matching-Pattern") ); assert_eq!(validator.get_standard_field_name("UnknownField"), None); } } debian-lsp-0.1.4/src/workspace.rs000064400000000000000000000463071046102023000150110ustar 00000000000000use rowan::ast::AstNode; use text_size::TextRange; use tower_lsp_server::ls_types::{Diagnostic, DiagnosticSeverity, NumberOrString, Uri}; /// Information about a field casing issue #[derive(Debug, Clone)] pub struct FieldCasingIssue { pub field_name: String, pub standard_name: String, pub field_range: TextRange, } /// Information about an UNRELEASED entry that can be marked for upload #[derive(Debug, Clone)] pub struct UnreleasedUploadInfo { pub unreleased_range: TextRange, pub target_distribution: String, } #[salsa::input] pub struct SourceFile { pub url: Uri, pub text: String, } // Store the Parse type directly - it's thread-safe now! #[salsa::tracked] pub fn parse_control( db: &dyn salsa::Database, file: SourceFile, ) -> debian_control::lossless::Parse { let text = file.text(db); debian_control::lossless::Control::parse(&text) } #[salsa::tracked] pub fn parse_copyright( db: &dyn salsa::Database, file: SourceFile, ) -> debian_copyright::lossless::Parse { let text = file.text(db); debian_copyright::lossless::Parse::parse_relaxed(&text) } #[salsa::tracked] pub fn parse_watch(db: &dyn salsa::Database, file: SourceFile) -> debian_watch::parse::Parse { let text = file.text(db); debian_watch::parse::Parse::parse(&text) } #[salsa::tracked] pub fn parse_changelog( db: &dyn salsa::Database, file: SourceFile, ) -> debian_changelog::Parse { let text = file.text(db); debian_changelog::ChangeLog::parse(&text) } // The actual database implementation #[salsa::db] #[derive(Clone, Default)] pub struct Workspace { storage: salsa::Storage, } impl salsa::Database for Workspace {} impl Workspace { pub fn new() -> Self { Self::default() } pub fn update_file(&mut self, url: Uri, text: String) -> SourceFile { SourceFile::new(self, url, text) } pub fn get_parsed_control( &self, file: SourceFile, ) -> debian_control::lossless::Parse { parse_control(self, file) } pub fn source_text(&self, file: SourceFile) -> String { file.text(self).clone() } pub fn get_parsed_copyright(&self, file: SourceFile) -> debian_copyright::lossless::Parse { parse_copyright(self, file) } /// Find field casing issues in copyright files, optionally within a specific range pub fn find_copyright_field_casing_issues( &self, file: SourceFile, range: Option, ) -> Vec { let mut issues = Vec::new(); let copyright_parse = self.get_parsed_copyright(file); let copyright = copyright_parse.to_copyright(); // Check header fields if let Some(header) = copyright.header() { for entry in header.as_deb822().entries() { let entry_range = entry.text_range(); // If a range is specified, check if this entry is within it if let Some(filter_range) = range { if entry_range.start() >= filter_range.end() || entry_range.end() <= filter_range.start() { continue; // Skip entries outside the range } } if let Some(key) = entry.key() { if let Some(standard_name) = crate::copyright::get_standard_field_name(&key) { if key != standard_name { if let Some(field_range) = entry.key_range() { issues.push(FieldCasingIssue { field_name: key.to_string(), standard_name: standard_name.to_string(), field_range, }); } } } } } } // Check files paragraphs for files_para in copyright.iter_files() { for entry in files_para.as_deb822().entries() { let entry_range = entry.text_range(); if let Some(filter_range) = range { if entry_range.start() >= filter_range.end() || entry_range.end() <= filter_range.start() { continue; } } if let Some(key) = entry.key() { if let Some(standard_name) = crate::copyright::get_standard_field_name(&key) { if key != standard_name { if let Some(field_range) = entry.key_range() { issues.push(FieldCasingIssue { field_name: key.to_string(), standard_name: standard_name.to_string(), field_range, }); } } } } } } // Check license paragraphs for license_para in copyright.iter_licenses() { for entry in license_para.as_deb822().entries() { let entry_range = entry.text_range(); if let Some(filter_range) = range { if entry_range.start() >= filter_range.end() || entry_range.end() <= filter_range.start() { continue; } } if let Some(key) = entry.key() { if let Some(standard_name) = crate::copyright::get_standard_field_name(&key) { if key != standard_name { if let Some(field_range) = entry.key_range() { issues.push(FieldCasingIssue { field_name: key.to_string(), standard_name: standard_name.to_string(), field_range, }); } } } } } } issues } pub fn get_copyright_diagnostics(&self, file: SourceFile) -> Vec { let source_text = self.source_text(file); let mut diagnostics = Vec::new(); // Add field casing diagnostics for issue in self.find_copyright_field_casing_issues(file, None) { let lsp_range = crate::position::text_range_to_lsp_range(&source_text, issue.field_range); diagnostics.push(Diagnostic { range: lsp_range, severity: Some(DiagnosticSeverity::WARNING), code: Some(NumberOrString::String("field-casing".to_string())), source: Some("debian-lsp".to_string()), message: format!( "Field name '{}' should be '{}'", issue.field_name, issue.standard_name ), ..Default::default() }); } diagnostics } pub fn get_parsed_watch(&self, file: SourceFile) -> debian_watch::parse::Parse { parse_watch(self, file) } pub fn get_parsed_changelog( &self, file: SourceFile, ) -> debian_changelog::Parse { parse_changelog(self, file) } /// Find UNRELEASED entries in the given range that can be marked for upload pub fn find_unreleased_entries_in_range( &self, file: SourceFile, range: TextRange, ) -> Vec { let parsed = self.get_parsed_changelog(file); let changelog = parsed.tree(); // Determine target distribution from previous entries let target_distribution = crate::changelog::get_target_distribution(&changelog); let mut results = Vec::new(); // Use the new efficient entries_in_range API for entry in changelog.entries_in_range(range) { // Check if this entry has UNRELEASED if let Some(dists) = entry.distributions() { if !dists.is_empty() && dists[0] == "UNRELEASED" { // Find the exact position of "UNRELEASED" in the entry's syntax tree let entry_text = entry.syntax().text().to_string(); if let Some(offset) = entry_text.find(") UNRELEASED;") { let unreleased_start = offset + 2; // +2 for ") " let unreleased_end = unreleased_start + "UNRELEASED".len(); // Convert to absolute positions let entry_range = entry.syntax().text_range(); let abs_start = entry_range.start() + text_size::TextSize::from(unreleased_start as u32); let abs_end = entry_range.start() + text_size::TextSize::from(unreleased_end as u32); results.push(UnreleasedUploadInfo { unreleased_range: TextRange::new(abs_start, abs_end), target_distribution: target_distribution.clone(), }); } } } } results } } #[cfg(test)] mod tests { use super::*; #[test] fn test_parse_copyright_with_correct_casing() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/copyright").unwrap(); let content = r#"Format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ Upstream-Name: test-package Source: https://example.com/test Files: * Copyright: 2024 Test Author License: MIT "#; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_copyright(file); assert!(parsed.errors().is_empty()); let copyright = parsed.to_copyright(); assert!(copyright.header().is_some()); assert_eq!(copyright.iter_files().count(), 1); } #[test] fn test_parse_copyright_with_incorrect_casing() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/copyright").unwrap(); let content = r#"format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ upstream-name: test-package source: https://example.com/test files: * copyright: 2024 Test Author license: MIT "#; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_copyright(file); assert!(parsed.errors().is_empty()); let copyright = parsed.to_copyright(); assert!(copyright.header().is_some()); assert_eq!(copyright.iter_files().count(), 1); } #[test] fn test_copyright_field_casing_detection() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/copyright").unwrap(); let content = r#"format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ upstream-name: test-package files: * copyright: 2024 Test Author license: MIT "#; let file = workspace.update_file(url, content.to_string()); let issues = workspace.find_copyright_field_casing_issues(file, None); // Should detect incorrect casing for format, upstream-name, files, copyright, license assert!(issues.len() >= 3, "Expected at least 3 field casing issues"); // Check that we detect specific fields let field_names: Vec<_> = issues.iter().map(|i| i.field_name.as_str()).collect(); assert!(field_names.contains(&"format")); assert!(field_names.contains(&"files")); assert!(field_names.contains(&"license")); } #[test] fn test_copyright_diagnostics() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/copyright").unwrap(); let content = r#"format: https://www.debian.org/doc/packaging-manuals/copyright-format/1.0/ upstream-name: test files: * copyright: 2024 Test license: MIT "#; let file = workspace.update_file(url, content.to_string()); let diagnostics = workspace.get_copyright_diagnostics(file); assert!( !diagnostics.is_empty(), "Should have diagnostics for field casing" ); // All diagnostics should be warnings for field casing for diag in &diagnostics { assert_eq!(diag.severity, Some(DiagnosticSeverity::WARNING)); assert_eq!( diag.code, Some(NumberOrString::String("field-casing".to_string())) ); } } #[test] fn test_parse_watch_linebased_v4() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/watch").unwrap(); let content = "version=4\nhttps://example.com/files .*/foo-(\\d[\\d.]*)/.tar\\.gz\n"; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_watch(file); assert_eq!(parsed.version(), 4); } #[test] fn test_parse_watch_deb822_v5() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/watch").unwrap(); let content = r#"Version: 5 Source: https://github.com/owner/repo/tags Matching-Pattern: .*/v?(\d[\d.]*)\.tar\.gz "#; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_watch(file); assert_eq!(parsed.version(), 5); } #[test] fn test_parse_watch_auto_detect_v1() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/watch").unwrap(); let content = "https://example.com/files .*/foo-(\\d[\\d.]*).tar\\.gz\n"; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_watch(file); // Should default to version 1 assert_eq!(parsed.version(), 1); } #[test] fn test_parse_changelog_basic() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_changelog(file); assert!(parsed.errors().is_empty()); } #[test] fn test_parse_changelog_multiple_entries() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.2.0-1) unstable; urgency=high * New upstream release. * Fix security vulnerability. -- John Doe Tue, 02 Jan 2024 12:00:00 +0000 rust-foo (0.1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); let parsed = workspace.get_parsed_changelog(file); assert!(parsed.errors().is_empty()); } #[test] fn test_find_unreleased_entries_in_range() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.2.0-1) UNRELEASED; urgency=medium * New changes. -- John Doe Tue, 02 Jan 2024 12:00:00 +0000 rust-foo (0.1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); // Search the entire file let full_range = TextRange::new(0.into(), (content.len() as u32).into()); let unreleased_entries = workspace.find_unreleased_entries_in_range(file, full_range); assert_eq!(unreleased_entries.len(), 1); assert_eq!(unreleased_entries[0].target_distribution, "unstable"); // Verify the range points to "UNRELEASED" let unreleased_text = &content[unreleased_entries[0].unreleased_range.start().into() ..unreleased_entries[0].unreleased_range.end().into()]; assert_eq!(unreleased_text, "UNRELEASED"); } #[test] fn test_find_unreleased_entries_multiple() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.3.0-1) UNRELEASED; urgency=medium * More new changes. -- John Doe Wed, 03 Jan 2024 12:00:00 +0000 rust-foo (0.2.0-1) UNRELEASED; urgency=medium * New changes. -- John Doe Tue, 02 Jan 2024 12:00:00 +0000 rust-foo (0.1.0-1) experimental; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); // Search the entire file let full_range = TextRange::new(0.into(), (content.len() as u32).into()); let unreleased_entries = workspace.find_unreleased_entries_in_range(file, full_range); // Should find both UNRELEASED entries assert_eq!(unreleased_entries.len(), 2); // Target should be "experimental" from the first released entry assert_eq!(unreleased_entries[0].target_distribution, "experimental"); assert_eq!(unreleased_entries[1].target_distribution, "experimental"); } #[test] fn test_find_unreleased_entries_partial_range() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.3.0-1) UNRELEASED; urgency=medium * More new changes. -- John Doe Wed, 03 Jan 2024 12:00:00 +0000 rust-foo (0.2.0-1) UNRELEASED; urgency=medium * New changes. -- John Doe Tue, 02 Jan 2024 12:00:00 +0000 rust-foo (0.1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); // Search only the first entry (first 100 characters should be enough) let partial_range = TextRange::new(0.into(), 100.into()); let unreleased_entries = workspace.find_unreleased_entries_in_range(file, partial_range); // Should find only the first UNRELEASED entry assert_eq!(unreleased_entries.len(), 1); } #[test] fn test_find_unreleased_entries_no_matches() { let mut workspace = Workspace::new(); let url = str::parse("file:///debian/changelog").unwrap(); let content = r#"rust-foo (0.1.0-1) unstable; urgency=medium * Initial release. -- John Doe Mon, 01 Jan 2024 12:00:00 +0000 "#; let file = workspace.update_file(url, content.to_string()); // Search the entire file let full_range = TextRange::new(0.into(), (content.len() as u32).into()); let unreleased_entries = workspace.find_unreleased_entries_in_range(file, full_range); // Should find no UNRELEASED entries assert_eq!(unreleased_entries.len(), 0); } } debian-lsp-0.1.4/vscode-debian/.eslintrc.json000064400000000000000000000006501046102023000171440ustar 00000000000000{ "root": true, "parser": "@typescript-eslint/parser", "parserOptions": { "ecmaVersion": 6, "sourceType": "module" }, "plugins": ["@typescript-eslint"], "rules": { "@typescript-eslint/naming-convention": "warn", "@typescript-eslint/semi": "warn", "curly": "warn", "eqeqeq": "warn", "no-throw-literal": "warn", "semi": "off" }, "ignorePatterns": ["out", "dist", "**/*.d.ts"] } debian-lsp-0.1.4/vscode-debian/.gitignore000064400000000000000000000000301046102023000163300ustar 00000000000000node_modules out *.vsix debian-lsp-0.1.4/vscode-debian/.vscodeignore000064400000000000000000000001411046102023000170330ustar 00000000000000.vscode/** .vscode-test/** src/** .gitignore .eslintrc.json tsconfig.json *.vsix node_modules/** debian-lsp-0.1.4/vscode-debian/LICENSE000064400000000000000000000236751046102023000153710ustar 00000000000000 Apache License Version 2.0, January 2004 http://www.apache.org/licenses/ TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION 1. Definitions. "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. "Licensor" shall mean the copyright owner or entity authorized by the copyright owner that is granting the License. "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). "Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. "Contribution" shall mean any work of authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contribution." "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. 2. Grant of Copyright License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. 3. Grant of Patent License. Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such license applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute patent litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You under this License for that Work shall terminate as of the date such litigation is filed. 4. Redistribution. You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modifications, and in Source or Object form, provided that You meet the following conditions: (a) You must give any other recipients of the Work or Derivative Works a copy of this License; and (b) You must cause any modified files to carry prominent notices stating that You changed the files; and (c) You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to any part of the Derivative Works; and (d) If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excluding those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the Derivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attribution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. You may add Your own copyright statement to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of the Work otherwise complies with the conditions stated in this License. 5. Submission of Contributions. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. 6. Trademarks. This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. 7. Disclaimer of Warranty. Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. 8. Limitation of Liability. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. 9. Accepting Warranty or Additional Liability. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. END OF TERMS AND CONDITIONS debian-lsp-0.1.4/vscode-debian/README.md000064400000000000000000000042671046102023000156370ustar 00000000000000# Debian Language Support for VS Code Language Server Protocol extension for Debian package files, including: - `debian/control` - Package control files - `debian/copyright` - DEP-5 copyright files - `debian/watch` - Upstream watch files - `debian/tests/control` - Autopkgtest control files ## Features - **Field name completion**: Intelligent completion for Debian control file fields - **Package name suggestions**: Common package name suggestions for dependencies - **Diagnostics**: Real-time validation of field names and syntax - **Quick fixes**: Automatic corrections for common issues like incorrect field casing - **Syntax highlighting**: Support for Debian control, copyright, and watch files ## Requirements The `debian-lsp` language server must be installed and available in your PATH, or you can configure the path to the executable in settings. ### Installing debian-lsp 1. Clone and build the language server: ```bash git clone https://github.com/jelmer/debian-lsp cd debian-lsp cargo build --release ``` 2. Either: - Copy `target/release/debian-lsp` to a directory in your PATH, or - Configure the path in VS Code settings (see Configuration below) ## Configuration This extension contributes the following settings: * `debian.enable`: Enable/disable the Debian language server (default: `true`) * `debian.serverPath`: Path to the debian-lsp executable (default: `"debian-lsp"`) * `debian.trace.server`: Trace communication between VS Code and the language server (default: `"off"`) ### Example configuration Add to your VS Code `settings.json`: ```json { "debian.serverPath": "/usr/local/bin/debian-lsp", "debian.trace.server": "verbose" } ``` ## Usage Simply open any Debian package file: - `debian/control` - `debian/copyright` - `debian/watch` - `debian/tests/control` The extension will automatically activate and provide language features. ## Development ### Building the extension ```bash npm install npm run compile ``` ### Packaging the extension ```bash npm run package ``` This creates a `.vsix` file that can be installed in VS Code. ### Installing the extension locally ```bash code --install-extension vscode-debian-*.vsix ``` ## License Apache-2.0+ debian-lsp-0.1.4/vscode-debian/images/debian-logo-256.png000064400000000000000000000425361046102023000210260ustar 00000000000000‰PNG  IHDRÎûŠI´ cHRMz&€„ú€èu0ê`:˜pœºQ<bKGDùC»tIMEê (;wcâG%tEXtdate:create2026-03-02T10:40:59+00:00ª5Fû%tEXtdate:modify2026-03-02T10:40:59+00:00ÛhþG(tEXtdate:timestamp2026-03-02T10:40:59+00:00Œ}ߘctEXtsvg:comment Generator: Adobe Illustrator 10.0, SVG Export Plug-In . SVG Version: 3.0.0 Build 77) ´™ÃCÏIDATxÚíçdWzÞsO÷ä8Ìä’Ã].w%í®´ÒÊ‚lÃ0пå¿À_†Ã€ Æ­°J»$‡™Ã0“zR÷LçÊžóô¹Us«»rUWÝ(tª¾UuîyΛ߷‹ -‡«\)üÕ0px8*ò˜;Àcàà€ÿÜøk>köÇk ô6û ´ ›½ èú€!`ØVM ;ü­7ñ5ùXæ€eD‚\Áÿl+áûð?óÀLx½à0þž¡†ÈˆS{ô qIˆóhƒ “FIœR`ÒB6É+Àe$YN…ßÛþ8TÁõ-½ì9K¯;L´GzˆÍÏ9$ÎëÄnâø{¿ÿÁðü~$…–ÿ³„Èw‘ë!R/‰ê_2‰¤J—´1j4Žl„W‘±ÿ"Ð ¢—«+ñ(i›±+åo9Š_;iÜïµ¹»Ã5Ò=ˆ87‘ôºl'K¤$‘6Âc‹»:Ge'6ÎÇ4¹ŒÜɶQN!Õì82êû‰q›j^³”¿•ú¼j^sI£îð9ßFª¢U¸)ಋ‡¿mÓ艓âJN®×a¹{Ïo!wò[H²Œ¡• VvÓ^k˜CRd“(Q,M—o¯P\ê!’D–@¾FU8R«nzI( P/Òù'ÑI{šè&~øŠÇŒ=^ŠM$q¬ÂÝGÄy‚ì §á«=zIX*ûÊ·ÀÁ K®ª m—?~ŒH3Hôœ £uê¸C¦=hm†Ð¡²ŠâAw‘ôùI¢©ð7“Âq¤!â᳜ Éç(´ýfHÉ!@qI´No¿@±˜ *3ò;«ˆ(7i¾D’ÈîsÛŒƒáa›p'ü¯sòž%þgWZµ²ê$‰ÓƒN½H»‚ܳ‡‘Ô9OŒÊg( }èðñ×7‘Ã` I§!99µÐ«· |ü3p=üïÀíÝV§j‘\2GêLj¶Ë¯Âã1q²‡|Ò´ÕÚÔ &ÀNâ‘̃3qÒÖ²Å>þðOÈ[—Œùš»h)Ôî§I“Kˆ$çQîØÛáw“djY5H&¹V‚!äâ¿~¾ƒÜÜÓH…[B¨åì ¶Ø0)’ÆAÌ ¢[ù]—q´ßÑ÷Œ8ÍCä>ÊX¸‹Ès'|ÿ¥-ÓbÙÛí&qlüO £ßÒå RÑN"ÒdvLë`ÅÍF½y8‹ÂSáñI¡B¼¨Ù*[[œ´ ‰ãLä—?B’æ<"Ò(Ñ%ÚŸ» `;f ©dk(Ka9 ·áóðu1yfèÀm Iz‰éõ6F'‘”yÕ³üyÊ2Øá°‰ÜÔ? Œ…OQÝ}bYyÓjˆ²ª6‚Üʯ \@¢þ¤ž]Fj[†ƒÛœDçÍ8rî¼||„$Ñ"Mr_·´ÄÙ#¿¬éÀ‰"þ;HžDöÌe$‰2Ãÿà¡0#<éÚ^Dxÿ‘ç>±ICbSh>ï uì'hQg‘ sÅkÄgËP¶QñÝ'Hmû:üüÙD›Ð•­¥7WŠÄéA¥¿Œì—wˆ¥Êˆ@ÉÔŽ í… šÎ#—õÇáñ9J>]*ü‡z‘è Ø8=È|é¹ ÒœE®Ç?ˆHf ;ßEt]ıdñ˜ÕW…&‘ìºãȽýHÍí!f+$tóÞúÂ:81ˆbAÏQ*O]Ñ’ÄI‘4. þ%"Έ4£ü˜ŒÝ±«HÝ\ ŸëXølóÄÖO‹È›´F$’ d{ÎÕ«ÉäÊtÀ¸Ì{$ü¾š¼fc…ÆPþáYäy³íSW´ qŠ8úÃG_~‰ …ß;a°•áâ°5¤f$›n ‡¿Í¡Z–‡ˆ HíèFÑs—/Ï›jøZÉF„Θè#žÂîŠ3¼Žn†è#È—R&ž%”“3ý¼VÁJ§:”ølnè˜C’gý*WòÒuj¥ºµ qŠà02þ?@µ2¯ëeŒVº™Å0‹ŒØû¨àk¹ÑßA7ù[Ü»‹Òƒ\°9äfŸ'¿ëŒ¥M²© ×"¹ù݆j9N†‰-«¬î˜h&ò8"× ÑŸ¤µƒÆ½hŸXÚŽ"éóaX׺ä¹58E2šm³¼Œ\n‘Tíüÿ­àîï&f„Œ+y·‘„~DTióö[5Ò§éÄIAª“yø)’6/…EÙ«ÓK«`Å”ž"IñI›/i ›êjÈnT‹ò=±Ó:u*è*¢÷{Æ=BDˆˆ|’h’ß¹´’vXµ†ÉÛ‡>Ön!|®OÃÚÎ×òE[‰8µH…ù+Dšä«f­ŽED„kÄN™&‘Õ­ª–œ Ÿ{˜BŸWÙ l[?- âßB®Þ¡ðp=Óy”qþÚ¨­t™@'_ —£fj[Ó>tÁÉ×…N´¨þÿ×À¿GÞ³V†Çw¸©ß Š/üywÜ fšŸÑ[RJÎ@{i?C1µb;à Zë[þÊ]C‡Ø<5è× ǧÄQ¤÷ÿJÒ<Ùì7V61nkHî…ïÒ³iìô‰í&7Ñ=r)ÀÛÈÓJÄBÚKªÃúßHÒoUsQh0qŠœù${•ü§ˆ Ÿ†Ç·á碫8õó$é°>×*R=¿!Þ»ËHâ!Y} &ÝÙFÊi<Þ×#ä|yŠî]žÃ œ{ÒL‰ã¶Ag‘Øÿ:±ZÁà,[è&Ø|Ÿ#ô’¤Ø{ι°Ñ¶‰RÈ^«Yä;EtgŸ yYê&ìr0ý2üîcd®íñY÷DCˆSÄå<„N¨7?GùgÇõžŠ ·ÇÏiÖydàÿ€N³5: VÝ#×ï7ˆ,o uû}dû4‹8öÂæ›ýˆDÈ²éžÆýÈÔ¬MÚ<WP²æ[HOn›+9íl…˜ÞâÉfΤù,|õP'à`J™RQðÙ¶í«\qò¥'­†¯ D)Ôè¢ÂdLkí¹Md‡>FZ HžFoT1T„öЩt’ÖjZŸE5înëê™1‹ÄÙ› H_~ž»Ië§ÿ4ËÈIâ&íߣö?¥¹Õ¸î«wIÃ$ ¯Q¦Ðhâô#õì”JóÓð}3Ì!óc‰h<>Fê× ñ$O<æhrùn+ÁŸû*W¶ˆë5ƒ•.d ;=f€è@hdH¤ Ú—ÑA·Œ¾E`õ*WJ¾&Î"ÌŸ"Is¸Á¯_ˆ%b„ü61Wl!<ÉWל¨éÇ®[³S ³,½£˜Ö*ra€ìÚ šÓ“»9,rè^?@÷ÖêvIZC݈SÄ!pyÎ~ƒ6ÃhL¶_}‚ ÚÏ‘Èþ©i®‹É¥<àÅ¡L¹4Û.{^B¬GÄt£ä&vŠLÒ©7zÐ~ pgFØ÷~6Jâ #ÙkÈ-xŠæž9öâ¶C×Ã×›èææ,3"”‡„Êæ_í%ô à·Hr_A’ç$q"D#œ…t™ nŒhµö‘Àï¡Õy{{æµÕœ8)*Z/RÑÞBÕ›g‰TÍÀ6ºw‰3. “.µDr-{bƒXÚìú × ?;c¼ÞpùÊ!¤6¾ó ’Œ+{ýs=%Ž=&®‘ðh4—8Ö½mÇ4:¹ã¢¾m#÷³èžô"µm„Ƈ&Ž¡é.ˆ{D N1[§fÄ)’‡v9~„Tµó4¶Îݪ@r Å,±Ùs\2Ô){c›è½Ì!Û@øþq|}£¢ w†ú¢Hõ©Ç.#ÉsíË ®èC6ø;ˆ8Ëá½½Ù^§Õdœ@^ GŠ©žFFè±úoùì z™åäBd¨?ÒÖù*W<ôŠ¥ £Ãî]šCœó(H¿„Rלß5„Üs—Q΃ôÎZd ì T™¿G΀Y!ë[ÖéX#6ô +W–‹ýö’Éè•îEp8Ý›r±udx/¢“:|¿Fœ~u‚ØCà}䆞¬rQ6‘Msé‘×P`sši2IÓ9H‘<¶·¿F’æ2*AÅe’½òÖ‰d<‚rÖzÐþŸC¦Íi ·8;H¬#Æ­†‡ƒ‹ÉɆÆr\¿¯k(˜ù/ÄÀæ4‰|³ ÅýÙá?¥òÒ÷p-7Ìw;ÜAd³¿‹ö·SͧN“8öU¢©p/#IóAøZ­[p›8'óKDœëH=[®âºÚî‡çŒåGhó{–R¹*Û’4w‘‘ã%bóùs]wpæÀ^êOBLv#1õ*…>Cmb5sÈGÿ9²iì=Ûu{gêY†l!Íè;ä][Gä)§„ÅdN"•¬©dáoSH€X`ŒÃ#Š&y¦¸{‹¼G¨<í!™{6ƒ†2ý# p=$s9ï‹"íá€L‹HÍrþÚåç25ž ½=Š$Ø<ùöý"ÎڣߕRã grÚð1bW’JCì}Œ$ͧÈà›"s9—„NZ›"=ÚVŠåÍÿV™—õ`³ äÜú "ÎÇh>B{Ó™n{¼äJ!ŽËŽ#âŒR»æ1Êø¥Óx4Dæ()Cˆ»»T­£o“bLP®!íd„X9Z.¼·/#Oñ›ˆˆÿ ©‚…õê¢Àk\ qúi.#B2VSnàÉ7o²>A>ùû$êºè¬9 HÑM,.ôõ‘ðÕ³65lÒ$ï×ÞE‰Hº¯\åŠg{zÃA(5ßDö±Uªy”îUªÝíõq0,|ý><î œÈ$ÍŽ‡µî‡ÒˆÓ °÷P1Q5=~·Â‡½ƒtEœ#먹¬VŒ¢pÀ)äœq­È8r£ö%ž_x¨™ ›ˆ$óH¹KôPÍ;öÉ¿Œ¤Ã£°6NJ®ɽ¾ƒ÷§hߎ‡ký½áTKNö‰äì®îo!]¯š)ÿ÷òNÜD*[Ç#Eºô 9ˆÖ|ÅN!þ%¤›Ÿ¿·ä)¶á“‘ðedßE§«ÇÍÛ½ë¹@ëW¹âéÙ­*…6áï„uè£râ8Àù&rÜBÚÑBx>Dž#–8HÇ› ‹¶Û;é€'Ñ:]Å›é¥ß!Úd¥ièB÷ãRß@–gÉL  r”xÏ\öQŠê܃6È`âz—Щ=Ïšâ¹6îãÐt)T$›à1Ê_;Du™Ó=Ä,—Æ, Ésqc8oâ £›t\³ˆ8fØ1Džjg8.¡îClêØrç"ƒ·@¤q6î{(Cã=¤"ï ›éÍo»¦arE~gÕï(Ñ«äÚ§Z}‰TÃ^D§¤T4¥¹ŽØ";ŽšJѤûb'Ñit€xZõq`Ì¢é0bÔiD˜^b:õép¡jú£- B~†ÜÎ…u=Žn´IϢܫÓÄÞbo„ßWÚÊk I {Ô\¿oâõãq;è^»—Ù:}ï ÄÛ)Dª²§4×.«€$Ïj×JÇñ¡ÕðùÇÂZ\{Q¹³GqسàdγHE«¶¢ó)Ê´þg¤ªe¤‰èA7è45ñ+$mÆ´Ÿ¤ü@³%í'én¥Í "G2»Ø3cFQjÕQ´?î{=\Ch§°Ÿr“àdͤ)U£Åt£{1‰Ö¾Ÿ8tçàd/ðóðýÄéË;èÆ½Œú£¡ºvO…7°U ͺ¢@Å1a&iÞþ%žB¸0É¢­ ´¦ËhM“}ïìvösH'Îhx¸²ÒÒ¨èî¾D´«ìŽýž¨¾m¹&ö­sþØ"q x¥ðÚÚþ ކyþv¸Ü‹¦ Ì£Så:bî2<ßAyi¨®´G.„ë? ³{7;ðv,¬ËÏQËàWÃïJYë¤{{vé,Ú<›$Oa|ƪZxô‰ìÞx§ˆU½ˆØgÂWWå~Al˜ÒlÇÁQ%­ì@é#ú9‚Íß‹R³ï…ÅŸF•°X—Ѫra†Qu&¼p#Û™6)ÑýÁ°¯¢ƒé7(}˜8¢ÄpÔ3ñ°[ßä~?‡n²‰S8-»ð}økÒÓ6ŽT´‹èÞ¿‚4ÓHu÷ÜÌbÚË,Aýn¢äÙ"u=*±Ò²j«¬0/S îEŒêpl‰ð!¢QZMyªG&Ü ¯Ñ©-l?œD*ðÏaÎÝüIõÌ›Þëw n Ä2â;Äȹ—¥ 'ˆÝE~a×txͳĀë‰ð^]«ò;D`§½4$â–¶­ó ¿ÏJébÐÓRÕëÑmRl#Q4žp(,ŠußJdû,J˜û=‰¼íŒ"ÙËb{æ‘ó%Ùßë–#¿=ÖGȹr“8ÐE†Iû¦dÍ È{Ü ª}SD'Â$yÞFž¾—‰Söæˆ]V× ¯Ý@鳌̀»a­]ÁY.Ü_`ñàYø|Nü|fâì ´Hbz†ê²Œmt“ï¡ØßD§¡—ØÈûçÄN-…±±ñ´÷ã:Õ?EÃu#UmÊ”ÿõ»í«\Ù ¿ ×ãð¾<=mmªsh¢À!dó<¤yñ¹U$ù#íX™ÿ¿‰Öö1Z÷‘p§HSrLg6Ië†î#u),FµDjZ»&=ˆ$瑚v‚XË”\Ói”†ô)ðOˆ4OHxÈZe­R\À–<Îq*U³Zz­ÝÑy3;KD2sÀ޳DÉïØØn ܳ9Œnn5³A­«O½sMZÈf¢-ú{Hõ(ò¼‡ÀÿA£@§ÜAó:º‡„íŠJNûZ`‘æ•Çpc÷·Âu’3@wH§Ù3΄®&/m Ž_!xŽ;9ëÄ)ìÔÛŠ?AvÍDâévÑOmÀ/ÃïvѪëUĽAóËD<q‰êSºœQñBú_óÙn³Ž~¤£^B7¼šfKÈ ú-"O§yÒœUþ:êA÷±ÍØDÆÿß"Ò|KÖ|±Vè"½t¼äjv9¸æ(^IâœB¹Sg(¿Ê3™%°ˆôôO‘ê±[Ñ"iè5C‘È»³ÊßG®çWˆ‰õÓ(ò[äI›çà©g­ 7à¨6þ˜LßÉólB$Î 2bß%¦€”‹m¤¦=C¶+I;ÉÝܱ?BÙ¯’¿–+èû¥Ðü@J&E¬S½Q,±µx?§º¶“Ä9nôé _ȇȕWqváævgmó.rÏ&oà"RcÿÅ :5ý¨ž¨iö…kÖG£ҩ9¤~‡6Æ#†bœ n§z ©fçè¿åЩ5KLé¸C®é€5jjE—¤î6”vwÎjôÂg(â} IN:Ií¾|e'±7ÐÁâ>f÷Pð°í³Ä›€½jÊA$#H›ÈãF/ù¬ª¦„`¢·hs$Å)àÌç×Ãã(‘8öÐÜFÞ³{(ý>ëZZ?TKO28‡4ˆatÏfå«\Ùê%Væ5«v L#¤“žž‡ú2q:]Râ8ð_‘ÄéÔB¾F¡Z©Ó…îç;HâL#mákÔoa±mx—ÚVƒ-³ñ€ÝN€]Ÿ.7>Mr䛿É_בëù‰µn‘ܳvC-$Î$Ò!Óc˜XuK/±¹CµiàÉfÞ‚$Ò=É!­÷\¹¡í¢_ ³\ô#èÜDêõSd§nˆãFÕºŽ“ãÞÚE†mA]€N’8N&»Î·íúY##Y¾‹:µrBRæ)²Ow»3%{TsC“¤i[Û&%;¸Ø¤û2ùÛÈæ»ƒ³$¼h&iFÑ&ìF7Oê¶ º ¾Îú»‡*>Dr$a[K›"ðXˆ£Â¿d>Úñ"Ûæy³ßl“Ћì¿WÃ÷7¨¾q`#±Eld³[öŸ6uº˜8+ÄN‘@{ž¨)£8&QBçe^Ìñs3ðëÈE¿PÒ‹´<:Æá÷К-¢e êæ©E’§Ç>'ö«ÛE5u7~â ‘N è "Ò\@Ä9‚ŒÉ hóHÄ?¡óúe÷¢äÖK(Ùõçˆ,‰^ÏN®Õ&xæÐž¾‹\ÐO(ØÛÕÇm¥ÌÊNñ¨9·ï%â´â›åÚ¯M§µüíAªëißzí•#T¿ïöCµ¥&ÎwHc˜¦À )ÌÁ©d¦g§Jww)Vø—CdYà`éôõ€çlö#ÒT+ö{­Z”,!/Ú}¤9ä5LIŽw¨9”=Gçç²k&)Þ-r“8I Ó°Cì©×ȃÃ}°Ë’„÷õ rE¿ S UÍÄiVºfÀC·N u$8ëÄàr'ªª7ßs¢®—÷ÕÍžñT ìxNJó‘dCõJ¤Ž™éÖ¨Bœ>â0Ûòo’çÓ,Qàiì0¸¯‚3Âï¢usó‹zÀ‡k¡º¹¡G8ÖTâØe·ˆ6J§èñ½Ä‰iCäg&‹ák«5lÜ{í9±yËqüy=ànMÃTÖpÆáÏrgÑ Ðjl\í$¸Ýœ1^œè°Ml`ïév çzýí·Hì“&}ÈÃ9Feõe.8ô쟱Ø3µç@¥ªš'/¡ÓµíˆS$÷ª‡8þ¢ŸüEµQ¼Lˆo·f%%"GlïÓ¼^ê«çw!tJ)ï1)<áÎ×tù  ûX UÍMª_h×ÚÆ°{Õm´ Ÿó;ÄêÁVÎͪŠ[4æPAžN7/ÕA`GÀLx¸©á±¥o^÷ZL[KŽëâ$½7þÙðŒ™-¢Î=H}½ItFPÀõ1©´,#òq¤ç4º‡¾ycCjáUÛ$ÎGì$â8Жö·^$†ˆ ï‰iê‹Jª?=…î!òþÍ =Ý…ì%›!öïÔÂ9àñdâœÏIä²¶‡§Ú¸B†½á S(Ú™Ò÷£µ„äxût=yÁN‚YÄÊ%ާývqö:Õ\z© Ö‘ÛŽ8)Ž“ä²hÔ®S ŽŽ‘äˆx¡Òv$»:Û£|þ ŽëtCml3µa‡[vU[ µiö€›•£þrGŠse˜Ô›Ä¾/-÷yÏV½Ý`ŒØGÝCŠw§0Ô«æjôèºf"é ¿w¶oÀ(Ñ]í®÷m=&j÷Ô(šnv˜r3Wç·–$@9 85yœ?ˆâPŽUnCmÒ»wЪ¶½TK"©V$6Œc5«Hª$‰cØA0Œ¿’±á-‰”òäÉz3|^IvgØFíaâÄôr¼jƒH:>Cõ7ëáOïß*‰€vµ¥Ós³:IMóȼ¢:Vˆ.´àcèô­w JÃ"qúP+¥¿B¶Ýg¨Že¹wQÿiãÉ–No£ÜR«a$7Pï;×NC*÷@øÝn,*y3+!›ttŠÚpú ´Ùã’Dÿˆ<Õ ëjUØp©5§Â÷Þ`ß'š×´6…¼nçüj y„½%NÒÌØ!–zwÍŽ~b;µ´‰còt6‘´yŽN¥‘”çt#âL"òô—zñ„âŒÓ!T1¹‚È3¾ÿ©@õ®€uƒ³”uÙÿ\øºM,Zóž^ ×x!Ë=Iœj:Ý´m³ÜD}†8þ±îðy ‘+¯YáAìà™rÊ¡þ ’¸? Mv ٣Ƞ®ç´9ï;;†ÔÅöÞ—îw~9/Ü4rš¨=<'ŽF,JœjЉÄy†TÒUÕ¤g¿ŽtükÉ?$¤ÀÁÃH-zm¼oÃzÜ'¶Œ­·mÓ$þ Ô†jˆ¸ñ÷ò¬í ƒïK”j3Gœ(чöô£pY ÊC2‰SLœÇÔ£'ЃN¿~tª%ÿx%NÁgó”ò‹ÀÄøÉSdÛ,#Õmµ²—ˆØ£;(ľm—š6RÂ%!Ž”üø8|¿IÕÞƒ²œšZVõm ÐnXG‹ù˜âÇúõXx ¡“¬ªdíBêèñð°Mp9îRŸcÎ!;‰ÈrÙYn’¾:0Éï§£„kŸmš8Þ#µ´Úà\§IW6N£°ŸW±—`›§½Ê rhÿE*Û6R¾G'v=z.¸õðŸ ~mg‘”8Šìœ½àÙžÓH•œ!:¬%\ Ͻ>à c9«ÍŽîTØ«ö ©$ûy]`u¹:_hþpÀàÄ*:µïó¼|¨þ²‰\iXÏÞÉeaã; ®r}‚6à÷(V5Š’ûÔ'(Þ…¤Í¤N&½˜p/¬#·ù5$Q’RÑšÁ DKÕIœê°ŠÈpƒ8®}0åy“İšÐ2-qË%m‚hè X@*Í"UÞ ;F‘¤9‹åæn"û>R37ˆîõ!â=²ª™Kû µ"N§J¬ d<~óʼn3„¤ÌçÈIPÏ `#‘LæÓ¹^ý\}I…ÂF÷¥¾_g 8 hzãHø©jsÅ.R‹h';ºÑfyŽì4•Íõ)w ÝôF£Ô ‰Ð…6Å(ùm“ §ðu[Ç„ï“Ý^òj­ N_Çñe³õ]ü/ŒQ/€ èÜh£I·2†‘³áLxþ·è@\,váZHœN%NÚdýDÎ~ëÐܨ¯ Uí!õOG™DöÀåðºG’-‹û¤!6‘ÿø¿(@8žÛ ž@“¼”§ðÞŸ ®I’gäX£am."•ÓÄ)zoj‘9൓àâm6wôÜB„è#ý@Džµ¡›2¾Ö«çZ"ë!tš¾‰"ì…‰‹nä7n¼r-ñYާ…{Ù õ.vðÛ]¾ˆì;iæÈ—ÃÄî8³ˆì²Zé>Ïõ~EQàB·¤¹ˆŠä~¾¾Lñ²dÔd.¡ÇÏl“—Iè°¸Mt¿ï‰B┸êEé-¤? o¶Ñ…R<^E7uÞSÄT¯ç°—N>žr-gœG£Ì—‘{Ÿ:fa‡æK•Bt£=õ&’4?Fkè\¸RPJ»átϾG÷ã!"MI£Y’Î7O·±XŠ ÒÙ¤¯#Ò|¼Ý†#-º‘q l“¨ÜF'œÇ:Þ#<×SÛ ×´y‡~Dœp0Gp²ÔRe« ­f¯]â{±ô›$Ú?A‡òbÿºB¸éÆqKÑžvÈ—.Î;,IëJgÝ´ÃÄ¢«ýàz”gÑ DW°¯å¯ãÈ>JºAo#É]³Î0­txéU7„$ô›H-{ÙGˆ’&í0ßA‡ÖÑÞî%øÚ«£grP@Þ~Ýo½zÿ´ˆnøQDšRˆ“¬÷>ClLá=íDœd÷Îat*^@Æ trÝFßs(Ÿ#·î2*ò%º~½ Ÿè%ÞènbªHê ÝJDHCÎ!“ÀO_!òœE$ØO=ÛBûö>1àþÏûÕJ%½ÀeíUÇQÖgèÄܯü4ù¡AbtØ`¼”ô“ƒ†’ÈwÐF†Ã÷›áûQâ Ð)¢Qj/–y'[æzŠõ«ˆ06ÔoQ0õ¸Õ c‘*¶ë†‰3•Ƥy ø’4gÂ:íE—™AΕïÑ!¾€öpjä¿Hž]Ù($Î •O6{7iÏv¸ÛÈ€üo éz°É¬€ÏPÄÝSf1Ü12­Yž{” ãbã(¦R¯ú–Fc¹—OǸ_þÙ4§Ñ¡=Ìþi\kè`º‚¶¶¯o3 ê5k48KÄÌÑJ‰ãi½íp“ ‘#N’>ŠN8§¶¿ALw_!Ö¾w#ÒÜE§â$Q ¶Zaõ'ô$~ggÍ °v•+y®ÒV“@{4dEÄpÊÌQ¤Ú¿ü>ûi)öþ®!Oæ7èúh‡èjH÷a¡ãôJˆ³Hã»í&qŒ-‰þ‡ðYßB›a¥v8Wí׈¶[îëGrH™ ¶X5ìö~m¶ãH•ùªsÍ^€½Ò½ ä¢Du ͱ°vgJ¼¼›> ký/a]î;Ñ,„ç¾ÐD°–HJœe¤OWJœdJ;Jœ$ž¿Gkæq HMØ@QîWi¦‰§ßcD¯{/1g‡«Ý>×ÕŽžÓr7\wعʕ¨fH¡"½œÝÏù5dôŸG‡ó ò$^AR{¿$aÆ5bØã£p¾F{ÖÒÊÉ™•æ_–„Bwô,•3Õ©Üm5¹ ˆ‘ëFýèÆ"ž¢›hcL!üЏ®ŽÓ|~^G6Ò$q$†É㸑-9¤Ê}ƒÒ|¦ÜªŽƒD”7QªÌOôœE{í¥yÍ ¿Üù:²i¾B’Æå͹ðù²÷’gݧ(” ' ö†ïÛ†<)ðz=E$Fªˆ×ѽ€ú~õ#És'<ÿ¢dß@§ï/êøÝˆ'‘Z÷Zøÿß"ņð&´FãDòþðžü"Ï)âThä,ñšn°ñ!"͈DK4iŸ%%Žç½”Òî( êãSwŽ6SÙRô÷%»éGÿ’Ï‘4†NÃChÓ Ëx×Úe ½l›¤sÀêNoX߉ðû5â‰þØöéÚU®ÔÅ›T`·$'Ÿ™à£á=AÁË?BFÿYbBi9pYú¤š¹Dà.:ä›vH$‰ã å*åoøäPŸçèÆ6¼Ž£ pÚͱìRGVÑ©ø –^B›épøù+´ ž»‚¾ž3Täõººó’R+ÈÛöÚX7‰54õD"‚盄÷u1¼ÿW‘Ûù<²a†¨¬[ì ²c> ëu‹8Y ©HÇ]J*I'7q^#z=îÒ³[D…¨,©mÏŒ!7ë,:=|jm.o¨áSÙwˆ§öq´)ÓNèqbêNwx Ç…F¤{FøÜמÑêû¼×Ôlˆv–¥‰ëúˆ®eKQK›3èþ¿Ž€sá3”j ÛFvvø2rùÿøg¤ª=§>-uËFá©Æ•<ŽNÒeÔìà;b]{[¹¦ST¶mt~Š6u2Ü‹ÈÓÖ÷•°V/¿@ªÞ}$q>Dž·×›ûhÁKw|mÚKh_#\gˆµóO‰ OPÞOpcub‡69ïÎ’¤IÚáoG(¿Ù¥c‰3ĸ××(FóCø\u h–‹BâlO'#m0l†ÃB·E'N»D“È¡Óp‰8þ®‡¨öΛ}w£jÆóhƒ~‡¼ewq6½8I¾«ºØ^·²½´„¤ÙDÌ[H²Ù²;{‹|/¨í–¾ð9!އ÷ûr‚œF÷»¨‚%C½ì½g RKŸýÍâ¼Ön‹ öþ5ŸNS^D7o†˜Wµ’'¡Åó­ÓR¨.(pU» „Ë\ µƒNüëD5iIžcÄZú^b÷ÈY¤žL…ç¦xþŸ×<éÖuhI­3Ä®üÈ&Õ·\âZVͬ–Û¾„l*ËIôÿäÂg½;ˆä¶ÿ^hdÒlIcJ÷ÜB'Ø%dÌ–³8Þ@y (:VÓ’›ÒeËvùÛ€ÿ´Îï Íý ‘ç:ð÷híÿqÒX©púÎ@øßWˆ™cmÅìœBû¦—(EGˆvM5°äÜ$fa|ÖÈÅ-]ŸFœ9¤Sž@7õp™×L.zÇ ¥²rÁ=ìrjK$÷*~¨²C§ù":]Óó8üm€ÒË=Âc¼„ç×ÞüNÉZDÎ’µðõk”:óYøœ»øV‘.iH#Î<òˆE^ JúØŸ?Og¤à” ÏŽ±TzŠÒ膤Â(Šý\οºŽlŒ‹̱ïŽS= Ÿå[$Y§‘:z/¬E²ßYK£8É$ºGä÷Ÿ*}DŒ½$q»ü7Ð:O¡ ã¬ßSÄnüçÐÆú ­áQÍ*,ݰ»8i5²*¹G\ìg·²iÍ#GÅïZæfŒît fɦgÝÌi*4ˆnúáZ‹Ô°ø ¢ˆûzm®ëHµšBå2ÂÏ!²\$ŽéëCð!±À­©q.þrÊS£a¢®^wç‰= í9²ùZ^-KCqœæ0GL)ƒèf;–ðCX¸zõG>ÈØ"vW¹ŽÖíM¤ªyŠÛE$¡!çpÚIRã@›Ð Šyà(ã÷¹2~gwûCd·\GN¦ûá=Û î‰tv…H¤Ç(gïnSž^=€Ü§Ëaá&‘ô:°‹T+¤;W¹â&o*Ox[!6«'6QqËÝcáçMt¯¾D¶C2Êo˜½bý‰G_â9ɼ3ˆª£Õ.'£ºld#ñsòá™ 7ѽ¿Gô Ú5߇f±¤;·Šòéà‹¥èÍnyäüSˆ8 ´HºD‹b Iz{ÒìxyÞÜ(Ñ]ÿM$«x¶£ÖÑ>XFä´GÌEbÊ5t,ìŽ=«”Öçì@¡RkNrÄ"¬ÇĤ¾R²[}zEºú»áçibýÈÆU®x¡¤Ž[+$ì;V‘mp©¹nvè¿ÙÖ"f"O‡ßm ÍºŽî™ƒ©ƒÄ^#‰¯îП$޵—Á¯mUÛ*þjòø5Þ´YØ‹‹HäžA’æ奅÷¡Üµ¿Däñ\ùÈv̦gº¶ /g^Ì»ëÜF©(žèv%TG©µWZ&³»ëË 1 ;™)|سµxØCfUl…|m¤£cìE„%”7t8{g-øQ¤_—[êFs6T­*8Óv© ߣÍ1Ïòã×E6ZµNFÝ-Q–9®£µBš@ëG$ó˜ Û%.áv¾šcB…¤±rf–˜4ú¼à¹mO”BìE7Fx€­¯˜S@£y‘g Ýä-tS;–8i("…'qÃpÛ,ãĤÑóÈ‘p­¹“D-]¼é‹IˆÄqÓ¿;HU´3ÀFGb/âl")à&…ÕxÄ’%À®5÷ÛDj„Öñ;î+D‘Ï¿l]åŠm–.$Ñ“YÌSèÐ;¤Î!´æÉ‘ItÒ[j7´ïÏ21é|»å¯ö"Ž Ô¤[1yUƒ.$qÞ@’Íõ"Ö£;>æS<žÐ¶‡ËB¾CRÈ}Ìœ êZW˜&§7;EfèU[#º˜]× C§šŠR¼dÕ}çtõjàˆ®ñxƒX»oeHAõÍäÙD‡Ü4ù9kÉ h2š €BŒßØJV‹ÖµOÙAà ÄIÉ«rÊOÑ¿NmˆcL wµ=l3txnÛ^(C}õXĤJìܶdRhaÆ@2‰´£{¡‰³ˆs˜¹{´„ÿ+ãÈk×^ç3 rÛ²Vö±áXc4i*wÒIK»nv?J#Î&24o µª’Œé½`ý{ yÛ¡|§ŽÖ¡«Á»Ò!É PJúù61xiðÖèB^¡ÃÄ*ÈRç=fÈÐp”BëÇ.xJìÚR‹“Ë= –‰ º/Q|ºp† MG9¹gHâÜ N"Kv;©ËħQ”oÕ…â³Í^  ÒPq¶ÄùyœÞQ-Ö‘ õØå³¶>C‡ (qRÜÒ9A¾Ž6öE¤RU «‚sˆ˜3(•d¾Ù‹“!C1”#qÜ<îrGÿŒÚ*{ˆ3ìo¢xÑq@P† -‡rš:¸Á4²G# än&•ÂîO"OÚ|xtb3à åÎ+˜b>…–½ÈIPi6Á0JFÜBöÓ1¤¶U2n$C††`_âå·€Êz¿AI„#TNœžpÓÈý"Ìîð  Z •HPî·Ä)aç‰3[*Å êâ¿®ë>Ë2´*%Î ’8}¨ Ë2Õç¯õ£vHãÄÑçSÀj§—Ugh=TÚñÑ™P…¨KŸ«1è»yŽ"7÷;¨ÜÀÓÆ2dhTº!aë>Ó·ªéXãLÝn”³ö*"ÎÑ*Þg† uAYªZJPÔb=²ÜQÿjÐMÌ_[!sKghATj㛨‰C?"ÎeÔª8Ðú-êAL¬k¯Ä6É›AtÛ¸î6ìþ7{Ý2t8ª•8Æ:ªÓùÙ9ƒÄY-å¢ CH¸ŒJ fPwÿÔ¶Lúdh4*’8Ãcþ†Ç…’§ Iž^Ô.÷,•—ôKª=´ÕãÏ×)Pá2 ”¡Ñ¨•Äå—¹¿ñûT—gÖ®s©€s׎oˆ—¡¹¨e|deKßGU¢7™Êu'{bu1¥g)ÙQ?C†¦¡ÅçÀ'Àÿ>Gym•b%{^GnïE:¸_q†ÖA-U5c eMw#·ô9$1,-ÊqQo½jKd¤ÉÐ"¨Š8EJÖQ<§õa{ˆˆ3ZÁë !'Ã"J*ý Ù?YN†¦¢ÇÃw#õê&JÃ9KùÍ=†P@uÙN_GèíNOÈ‚¢j[;í¢ˆ‹º/¼F?JÜ´ÊV*’‰ŸvE'§bYP4CãQ‰cl!Ã>‡Ès ©n•Àu?žrð "S2‰“¡Ñ¨'q¶‘‡m¥â|2N TŽ´ëMüßs4žÜSÉjÕQ4C†’Q3UÍH¨jÆVxÏ“@¶N¹YM±‚¼lVÝVyl™º–¡Q¨§Ä1vPõ5b2è)dø—‹>Ôç-d㬢ÊÓ%ÈT¶ C͉“RìÚØ÷´¹ˆJrÄÙ”û!9]ì0jê±M´u<,«ÛÉÐ4Bâ@ìQðeGž²W‰Ä)58:¼žû¹©·‰Ö }°R&…2Ôu#NJp4‡Ôª(:Œ þr\Ôž<€:¾‹¤Ù"ä4ÑQ°I–i¡Nh”Ä1Ö€ÛhSO¢y¢'¨ÌÞÞ ×CÞ»n"a–H82d¨%Mœ-$u¶„øe=ŸAöN9ÁQO­>„2CŒñ̇ÅfÈPsÔ܆”¬O3^FÜ# +-µîARgIžE¢×-sUg¨9"qRŒõäe3qÎ#oÛ!ÊÏ î!J¬q¤΢à虓¡h–Ĺ“WIúÂ×ä4('8êÿ³´…£³ÈY°é÷!C­Ðh§;¨}î?3žSž³ )¡Æ‘Ãa I´ëMþ|Ú•ö=« õ­Ù&/ü%ŠïLPZp4‰M$žþ+ðßP¢é®g-‹çd¨š-q ºŽ§€ƒ`ø ²]ÊArC ¥§‘£ ¯~'C†jÑ ÄÙ;s¨KÎ6²YŽ=eÝ”.»‰Ã}ßFRè&r ÉP 48)9mN›éBÁM{È^AYå¼×!Dš#'C†Z U$ÈYEA¿ARgÕàL–ù^‘t8\ó»pÝ%bÛÞ®«\Ék5•I  ¥¢!îè½P$8º…¤ëwúqú(@ÎiëG΂åÄÏî:>ÿY4C™h%‰c8£àAø¹Ù)ëhJ[9Cz»Q{ª?A6Ó¨È"Ë,±,!C†’Ñ4wô^HØ=c(«à-Ô„ýÑ?J šî…$qž£¸Î׈8Hšý€š&>  4SÛ2ì…V”8I¬¢M½¤Çr-;«z?t!µìRÓ&Ó  I‘íóI´ JB«ÇÙÔh³{Ï(rS»Ÿô^’Çé8&P‘È  7ˆ*ÛJxͬ ;Þhºs Ez´¹L`“Øx½—hä§¡+ñèÏósÝ@dp¹Ã7 ßK† …hu‰cl#ɳޤÏÊèF®êJæðt!ûéj²Ž’B§ÂëmCV†!-)qŒÉc©³‰ˆãŠOK’~ÊϪîF*\RѺÐa²Mlþ‘çÉ$Phqâ)å«Èe½€$O/r7–}qÁÿ¹»‰£âwëy2Òd0ZÒ½ªÓR³.¿þyÛ…¿UòÙV€þÊ^˜!ÎuÉ÷.2•­sq $NEËhc¯¡ØO¹ÍÝ ×ä"áûˆ˜›¤¸¬3 Ô¹8(Î48·m+|F*Ö$1³:G̬.…H½H];It}‹jz®5ûgh8â¤dUo"[g‹˜ƒö IŒóHuó¸‘ý`ç@/Ší8Û`ŠDÇœLEËpàˆSvÜB%ß¿F¶Ï%”1P®ZúøgàŸÂõví›,8šáÀ9Òk9ü å¶½Ô¯ãHú QZªÀ?¢\¶'H=B ìÓ«:#U{ãÀ9ÒPd´ÈÚèÓÈ[–CjÜ8¥§ÙK/?F™Ù‡å¶VžEúÂõ2ÇA{£]TµB¬£äЧˆ8 H•s°ô0rYïupŒ‡H2¯"‡ÃR¸Î¤:®­ç°z•+k¾P&}Úm¡ª"¡ºu¡Í•#¼†JÞC*ÜX—ÝBdùÅx¾Ae O‘DCÞ¸-ÔãàA"eÄi?´•ÄI±urDi3ƒÜÊwÃßN ÕÍ)5û¹­{­t9Î"Ûm$=Šb>NšAj]–ïÖ†h+â¤mÆÐW`ƒ˜$ºMì1};ýgïLë”ÖsÙ8cÀ…pí!d¹Õ*š{^7#K¡­ˆ³v4xü5𸌠Ûü¡4‡É0Šy¬âR×–÷ÍvÔïÃï²"¹6C[Ú8iHQ—ºQÃ÷€W tI’!t°”ÒÓÍ*á3bóÃ'(ô·(¾´D¢)b&}6ÚÂ] ŠÇm£ = ÿ)$9ÜYÇ]uJ9`º‰D=ìʤ˅×YN¾Ÿ £ª¥èh#¯ âü€$Ãsä[AnDéãªÑ4"%IæÊRI»ÂõÖv‚íµ‹L,t qŠ8@Ò`X{Ó… üë¨÷ôyÔÜð’"i¤é*ø ’X'qz±#wöTx½Œ0Cœ}àîžË(ÕæÊ8잢M4„HQH–BBõÇÑ#Ò[B’-W(y’ÈHÕºèhâlÌÜU®lË–aœ®3‡$Ð$r]'_+$NràÕ1â­ó¹…$Ï,±<"í}ehAtŒs ä¼åÐf^F²[È…}yÏ :úÙ;ÿ­'Ûù‹‡g…/ÜqzÑ­¿¢yZÃóĉˆ›8wy]I%ÿW;‡åȦ@¿r>,ìò߯tÏU¾iÛ}~ dóˆI*Á¿ÑbÏ»Élzd9 (^H÷ÈâšœâY³e£\'¥”I]å®[]C‹bÎü²-êhÒ†C±<Àø5[LbM•†’ujžy1’|-d„]Óˆ§Ö.*ÒÔ–Ÿl±Ç›Ã €y®Lý:ûý¨Vè#hîš ˜`ÂêDÞâÌyÌÑWbæ<í»ûZíc¼»¤/PN(«Ãj üºîÿ,6©f˺ZTÜM‹u2¹y¨Ød­ƒp ½(x ¶ª¥c÷"WÁ„Mõ²uÍÐ"örœêG1PüžsXJ—^ÏzbÝŽsXe±îAY*Öï0{^g}¬iµ(¡ÌáÞ“áºÉªIé~ɇp·h6ÐxrÌ—–*æõM¾` Õ\z²C‰Ç=îFùm?ä\°›Qº¦5å ‰ópGëê:Œœ› ¿EÁ*J¥‹ò¥úõ‹(ªÐ dõ+”3Š©‹wÿ§°À´ÉPa‡Ò0ùÜè˜ûª`uéAÉÉ×{m»{Œs]ÔØƒ"Ù­\úTX•lŽÒˆÜ­º¨nÑ3vÈ¥–h½GÑ-7 ûÃGä²)båæ(?:Ô¼¡Ÿ"#‚dV_”I üA=+ú!2µnŠÝZzÕQ£oë->6@äMœÅi ¹“K’Áð† ®˜2%Ê FÜÉ”áÌÔÞÃüÊJêbÒ¼uy°FŠ)Û8^Ï´Š_®E©%ü™TÔBÝýRÉ|3Íû–VXã2‰icX\Ì.Ó|ùA¿„©Ü6é.gñsüiê[î¶ø‹xë­‰‹V‹™,ëFA§¸ì>®—è¹Icga]YID$7»=ë«»™Ô×Ú2©ÃÅÝŽ½³tã cV¦ó]¯^òž¥•–ŒeaT’|ã:lÕ·Âî÷BÕ$ãSÏh±yµí—{ãªÚ$Ë$ù®±i³ˆiŸêtÈSpxn-Ôîá–Wt]PovÉ|Ø×»‡²îø¥‡²î“ Í8ú".³ü7ÊÙc[ÉŒðEdìÈÃлKøÚaìÈø¯9‡ÂÈÛú[¦_½ƒ©`hfÎósßôÏø'>S­Ÿ€¼#4cÙ,æÐ¸ð‰Äo€Caü'‡Yˆ–¾ñßÀ öšú¤¤­ß¹oñL‚ߘj |9¶ªÍÃ=÷@#‘~ûzœôMê÷ Ì‚3˜ó<À`óŽ3ƸSf4Ðôm-—Iíq1¥Üã¼?f±$J¿jïIEND®B`‚debian-lsp-0.1.4/vscode-debian/images/debian-logo.svg000064400000000000000000000151711046102023000205220ustar 00000000000000 ]> debian-lsp-0.1.4/vscode-debian/language-configuration.json000064400000000000000000000002251046102023000216710ustar 00000000000000{ "comments": { "lineComment": "#" }, "brackets": [], "autoClosingPairs": [], "surroundingPairs": [], "semanticHighlighting": true } debian-lsp-0.1.4/vscode-debian/package-lock.json000064400000000000000000004365161046102023000176020ustar 00000000000000{ "name": "vscode-debian", "version": "0.1.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "vscode-debian", "version": "0.1.0", "license": "Apache-2.0+", "dependencies": { "vscode-languageclient": "^9.0.1" }, "devDependencies": { "@types/node": "^20.x", "@types/vscode": "^1.75.0", "@typescript-eslint/eslint-plugin": "^6.x", "@typescript-eslint/parser": "^6.x", "@vscode/vsce": "^2.x", "eslint": "^8.x", "typescript": "^5.x" }, "engines": { "vscode": "^1.75.0" } }, "node_modules/@azure/abort-controller": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/@azure/abort-controller/-/abort-controller-2.1.2.tgz", "integrity": "sha512-nBrLsEWm4J2u5LpAPjxADTlq3trDgVZZXHNKabeXZtpq3d3AbN/KGO82R87rdDz5/lYB024rtEf10/q0urNgsA==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=18.0.0" } }, "node_modules/@azure/core-auth": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@azure/core-auth/-/core-auth-1.10.1.tgz", "integrity": "sha512-ykRMW8PjVAn+RS6ww5cmK9U2CyH9p4Q88YJwvUslfuMmN98w/2rdGRLPqJYObapBCdzBVeDgYWdJnFPFb7qzpg==", "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.1.2", "@azure/core-util": "^1.13.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/core-client": { "version": "1.10.1", "resolved": "https://registry.npmjs.org/@azure/core-client/-/core-client-1.10.1.tgz", "integrity": "sha512-Nh5PhEOeY6PrnxNPsEHRr9eimxLwgLlpmguQaHKBinFYA/RU9+kOYVOQqOrTsCL+KSxrLLl1gD8Dk5BFW/7l/w==", "dev": true, "license": "MIT", "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": ">=20.0.0" } }, "node_modules/@azure/core-rest-pipeline": { "version": "1.22.2", "resolved": "https://registry.npmjs.org/@azure/core-rest-pipeline/-/core-rest-pipeline-1.22.2.tgz", "integrity": "sha512-MzHym+wOi8CLUlKCQu12de0nwcq9k9Kuv43j4Wa++CsCpJwps2eeBQwD2Bu8snkxTtDKDx4GwjuR9E8yC8LNrg==", "dev": true, "license": "MIT", "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.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/core-tracing": { "version": "1.3.1", "resolved": "https://registry.npmjs.org/@azure/core-tracing/-/core-tracing-1.3.1.tgz", "integrity": "sha512-9MWKevR7Hz8kNzzPLfX4EAtGM2b8mr50HPDBvio96bURP/9C+HjdH3sBlLSNNrvRAr5/k/svoH457gB5IKpmwQ==", "dev": true, "license": "MIT", "dependencies": { "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/core-util": { "version": "1.13.1", "resolved": "https://registry.npmjs.org/@azure/core-util/-/core-util-1.13.1.tgz", "integrity": "sha512-XPArKLzsvl0Hf0CaGyKHUyVgF7oDnhKoP85Xv6M4StF/1AhfORhZudHtOyf2s+FcbuQ9dPRAjB8J2KvRRMUK2A==", "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.1.2", "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/identity": { "version": "4.13.0", "resolved": "https://registry.npmjs.org/@azure/identity/-/identity-4.13.0.tgz", "integrity": "sha512-uWC0fssc+hs1TGGVkkghiaFkkS7NkTxfnCH+Hdg+yTehTpMcehpok4PgUKKdyCH+9ldu6FhiHRv84Ntqj1vVcw==", "dev": true, "license": "MIT", "dependencies": { "@azure/abort-controller": "^2.0.0", "@azure/core-auth": "^1.9.0", "@azure/core-client": "^1.9.2", "@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": "^4.2.0", "@azure/msal-node": "^3.5.0", "open": "^10.1.0", "tslib": "^2.2.0" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/logger": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@azure/logger/-/logger-1.3.0.tgz", "integrity": "sha512-fCqPIfOcLE+CGqGPd66c8bZpwAji98tZ4JI9i/mlTNTlsIWslCfpg48s/ypyLxZTump5sypjrKn2/kY7q8oAbA==", "dev": true, "license": "MIT", "dependencies": { "@typespec/ts-http-runtime": "^0.3.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@azure/msal-browser": { "version": "4.29.0", "resolved": "https://registry.npmjs.org/@azure/msal-browser/-/msal-browser-4.29.0.tgz", "integrity": "sha512-/f3eHkSNUTl6DLQHm+bKecjBKcRQxbd/XLx8lvSYp8Nl/HRyPuIPOijt9Dt0sH50/SxOwQ62RnFCmFlGK+bR/w==", "dev": true, "license": "MIT", "dependencies": { "@azure/msal-common": "15.15.0" }, "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-common": { "version": "15.15.0", "resolved": "https://registry.npmjs.org/@azure/msal-common/-/msal-common-15.15.0.tgz", "integrity": "sha512-/n+bN0AKlVa+AOcETkJSKj38+bvFs78BaP4rNtv3MJCmPH0YrHiskMRe74OhyZ5DZjGISlFyxqvf9/4QVEi2tw==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/@azure/msal-node": { "version": "3.8.8", "resolved": "https://registry.npmjs.org/@azure/msal-node/-/msal-node-3.8.8.tgz", "integrity": "sha512-+f1VrJH1iI517t4zgmuhqORja0bL6LDQXfBqkjuMmfTYXTQQnh1EvwwxO3UbKLT05N0obF72SRHFrC1RBDv5Gg==", "dev": true, "license": "MIT", "dependencies": { "@azure/msal-common": "15.15.0", "jsonwebtoken": "^9.0.0", "uuid": "^8.3.0" }, "engines": { "node": ">=16" } }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.1", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.1.tgz", "integrity": "sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==", "dev": true, "license": "MIT", "dependencies": { "eslint-visitor-keys": "^3.4.3" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" }, "peerDependencies": { "eslint": "^6.0.0 || ^7.0.0 || >=8.0.0" } }, "node_modules/@eslint-community/regexpp": { "version": "4.12.2", "resolved": "https://registry.npmjs.org/@eslint-community/regexpp/-/regexpp-4.12.2.tgz", "integrity": "sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==", "dev": true, "license": "MIT", "engines": { "node": "^12.0.0 || ^14.0.0 || >=16.0.0" } }, "node_modules/@eslint/eslintrc": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/@eslint/eslintrc/-/eslintrc-2.1.4.tgz", "integrity": "sha512-269Z39MS6wVJtsoUl10L60WdkhJVdPG24Q4eZTH3nnF6lpvSShEK3wQjDX9JRWAUPvPh7COouPpU9IrqaZFvtQ==", "dev": true, "license": "MIT", "dependencies": { "ajv": "^6.12.4", "debug": "^4.3.2", "espree": "^9.6.0", "globals": "^13.19.0", "ignore": "^5.2.0", "import-fresh": "^3.2.1", "js-yaml": "^4.1.0", "minimatch": "^3.1.2", "strip-json-comments": "^3.1.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/@eslint/eslintrc/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@eslint/eslintrc/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/@eslint/js": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/@eslint/js/-/js-8.57.1.tgz", "integrity": "sha512-d9zaMRSTIKDLhctzH12MtXvJKSSUhaHcjV+2Z+GK+EEY7XKpP5yR4x+N3TAcHTcu963nIr+TMcCb4DBCYX1z6Q==", "dev": true, "license": "MIT", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, "node_modules/@humanwhocodes/config-array": { "version": "0.13.0", "resolved": "https://registry.npmjs.org/@humanwhocodes/config-array/-/config-array-0.13.0.tgz", "integrity": "sha512-DZLEEqFWQFiyK6h5YIeynKx7JlvCYWL0cImfSRXZ9l4Sg2efkFGTuFf6vzXjK1cq6IYkU+Eg/JizXw+TD2vRNw==", "deprecated": "Use @eslint/config-array instead", "dev": true, "license": "Apache-2.0", "dependencies": { "@humanwhocodes/object-schema": "^2.0.3", "debug": "^4.3.1", "minimatch": "^3.0.5" }, "engines": { "node": ">=10.10.0" } }, "node_modules/@humanwhocodes/config-array/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@humanwhocodes/config-array/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/@humanwhocodes/module-importer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/@humanwhocodes/module-importer/-/module-importer-1.0.1.tgz", "integrity": "sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==", "dev": true, "license": "Apache-2.0", "engines": { "node": ">=12.22" }, "funding": { "type": "github", "url": "https://github.com/sponsors/nzakas" } }, "node_modules/@humanwhocodes/object-schema": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/@humanwhocodes/object-schema/-/object-schema-2.0.3.tgz", "integrity": "sha512-93zYdMES/c1D69yZiKDBj0V24vqNzB/koF26KPaagAfd3P/4gUlh3Dys5ogAK+Exi9QyzlD8x/08Zt7wIKcDcA==", "deprecated": "Use @eslint/object-schema instead", "dev": true, "license": "BSD-3-Clause" }, "node_modules/@nodelib/fs.scandir": { "version": "2.1.5", "resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz", "integrity": "sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==", "dev": true, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "dependencies": { "@nodelib/fs.scandir": "2.1.5", "fastq": "^1.6.0" }, "engines": { "node": ">= 8" } }, "node_modules/@types/json-schema": { "version": "7.0.15", "resolved": "https://registry.npmjs.org/@types/json-schema/-/json-schema-7.0.15.tgz", "integrity": "sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==", "dev": true, "license": "MIT" }, "node_modules/@types/node": { "version": "20.19.35", "resolved": "https://registry.npmjs.org/@types/node/-/node-20.19.35.tgz", "integrity": "sha512-Uarfe6J91b9HAUXxjvSOdiO2UPOKLm07Q1oh0JHxoZ1y8HoqxDAu3gVrsrOHeiio0kSsoVBt4wFrKOm0dKxVPQ==", "dev": true, "license": "MIT", "dependencies": { "undici-types": "~6.21.0" } }, "node_modules/@types/semver": { "version": "7.7.1", "resolved": "https://registry.npmjs.org/@types/semver/-/semver-7.7.1.tgz", "integrity": "sha512-FmgJfu+MOcQ370SD0ev7EI8TlCAfKYU+B4m5T3yXc1CiRN94g/SZPtsCkk506aUDtlMnFZvasDwHHUcZUEaYuA==", "dev": true, "license": "MIT" }, "node_modules/@types/vscode": { "version": "1.109.0", "resolved": "https://registry.npmjs.org/@types/vscode/-/vscode-1.109.0.tgz", "integrity": "sha512-0Pf95rnwEIwDbmXGC08r0B4TQhAbsHQ5UyTIgVgoieDe4cOnf92usuR5dEczb6bTKEp7ziZH4TV1TRGPPCExtw==", "dev": true, "license": "MIT" }, "node_modules/@typescript-eslint/eslint-plugin": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/eslint-plugin/-/eslint-plugin-6.21.0.tgz", "integrity": "sha512-oy9+hTPCUFpngkEZUSzbf9MxI65wbKFoQYsgPdILTfbUldp5ovUuphZVe4i30emU9M/kP+T64Di0mxl7dSw3MA==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/regexpp": "^4.5.1", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/type-utils": "6.21.0", "@typescript-eslint/utils": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "graphemer": "^1.4.0", "ignore": "^5.2.4", "natural-compare": "^1.4.0", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "@typescript-eslint/parser": "^6.0.0 || ^6.0.0-alpha", "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/parser": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/parser/-/parser-6.21.0.tgz", "integrity": "sha512-tbsV1jPne5CkFQCgPBcDOt30ItF7aJoZL997JSF7MhGQqOeT3svWRYxiqlfA5RUdlHN6Fi+EI9bxqbdyAUZjYQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/scope-manager": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/scope-manager/-/scope-manager-6.21.0.tgz", "integrity": "sha512-OwLUIWZJry80O99zvqXVEioyniJMa+d2GrqpUTqi5/v5D5rOrppJVBPa0yKCblcigC0/aYAzxxqQ1B+DS2RYsg==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/type-utils": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/type-utils/-/type-utils-6.21.0.tgz", "integrity": "sha512-rZQI7wHfao8qMX3Rd3xqeYSMCL3SoiSQLBATSiVKARdFGCYSRvmViieZjqc58jKgs8Y8i9YvVVhRbHSTA4VBag==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/typescript-estree": "6.21.0", "@typescript-eslint/utils": "6.21.0", "debug": "^4.3.4", "ts-api-utils": "^1.0.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/types": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/types/-/types-6.21.0.tgz", "integrity": "sha512-1kFmZ1rOm5epu9NZEZm1kckCDGj5UJEf7P1kliH4LKu/RkwpsfqqGmY2OOcUs18lSlQBKLDYBOGxRVtrMN5lpg==", "dev": true, "license": "MIT", "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typescript-eslint/typescript-estree": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/typescript-estree/-/typescript-estree-6.21.0.tgz", "integrity": "sha512-6npJTkZcO+y2/kr+z0hc4HwNfrrP4kNYh57ek7yCNlrBjWQ1Y0OS7jiZTkgumrvkX5HkEKXFZkkdFNkaW2wmUQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "@typescript-eslint/types": "6.21.0", "@typescript-eslint/visitor-keys": "6.21.0", "debug": "^4.3.4", "globby": "^11.1.0", "is-glob": "^4.0.3", "minimatch": "9.0.3", "semver": "^7.5.4", "ts-api-utils": "^1.0.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependenciesMeta": { "typescript": { "optional": true } } }, "node_modules/@typescript-eslint/utils": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/utils/-/utils-6.21.0.tgz", "integrity": "sha512-NfWVaC8HP9T8cbKQxHcsJBY5YE1O33+jpMwN45qzWWaPDZgLIbo12toGMWnmhvCpd3sIxkpDw3Wv1B3dYrbDQQ==", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.4.0", "@types/json-schema": "^7.0.12", "@types/semver": "^7.5.0", "@typescript-eslint/scope-manager": "6.21.0", "@typescript-eslint/types": "6.21.0", "@typescript-eslint/typescript-estree": "6.21.0", "semver": "^7.5.4" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" }, "peerDependencies": { "eslint": "^7.0.0 || ^8.0.0" } }, "node_modules/@typescript-eslint/visitor-keys": { "version": "6.21.0", "resolved": "https://registry.npmjs.org/@typescript-eslint/visitor-keys/-/visitor-keys-6.21.0.tgz", "integrity": "sha512-JJtkDduxLi9bivAB+cYOVMtbkqdPOhZ+ZI5LC47MIRrDV4Yn2o+ZnW10Nkmr28xRpSpdJ6Sm42Hjf2+REYXm0A==", "dev": true, "license": "MIT", "dependencies": { "@typescript-eslint/types": "6.21.0", "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^16.0.0 || >=18.0.0" }, "funding": { "type": "opencollective", "url": "https://opencollective.com/typescript-eslint" } }, "node_modules/@typespec/ts-http-runtime": { "version": "0.3.3", "resolved": "https://registry.npmjs.org/@typespec/ts-http-runtime/-/ts-http-runtime-0.3.3.tgz", "integrity": "sha512-91fp6CAAJSRtH5ja95T1FHSKa8aPW9/Zw6cta81jlZTUw/+Vq8jM/AfF/14h2b71wwR84JUTW/3Y8QPhDAawFA==", "dev": true, "license": "MIT", "dependencies": { "http-proxy-agent": "^7.0.0", "https-proxy-agent": "^7.0.0", "tslib": "^2.6.2" }, "engines": { "node": ">=20.0.0" } }, "node_modules/@ungap/structured-clone": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/@ungap/structured-clone/-/structured-clone-1.3.0.tgz", "integrity": "sha512-WmoN8qaIAo7WTYWbAZuG8PYEhn5fkz7dZrqTBZ7dtt//lL2Gwms1IcnQ5yHqjDfX8Ft5j4YzDM23f87zBfDe9g==", "dev": true, "license": "ISC" }, "node_modules/@vscode/vsce": { "version": "2.32.0", "resolved": "https://registry.npmjs.org/@vscode/vsce/-/vsce-2.32.0.tgz", "integrity": "sha512-3EFJfsgrSftIqt3EtdRcAygy/OJ3hstyI1cDmIgkU9CFZW5C+3djr6mfosndCUqcVYuyjmxOK1xmFp/Bq7+NIg==", "dev": true, "license": "MIT", "dependencies": { "@azure/identity": "^4.1.0", "@vscode/vsce-sign": "^2.0.0", "azure-devops-node-api": "^12.5.0", "chalk": "^2.4.2", "cheerio": "^1.0.0-rc.9", "cockatiel": "^3.1.2", "commander": "^6.2.1", "form-data": "^4.0.0", "glob": "^7.0.6", "hosted-git-info": "^4.0.2", "jsonc-parser": "^3.2.0", "leven": "^3.1.0", "markdown-it": "^12.3.2", "mime": "^1.3.4", "minimatch": "^3.0.3", "parse-semver": "^1.1.1", "read": "^1.0.7", "semver": "^7.5.2", "tmp": "^0.2.1", "typed-rest-client": "^1.8.4", "url-join": "^4.0.1", "xml2js": "^0.5.0", "yauzl": "^2.3.1", "yazl": "^2.2.2" }, "bin": { "vsce": "vsce" }, "engines": { "node": ">= 16" }, "optionalDependencies": { "keytar": "^7.7.0" } }, "node_modules/@vscode/vsce-sign": { "version": "2.0.9", "resolved": "https://registry.npmjs.org/@vscode/vsce-sign/-/vsce-sign-2.0.9.tgz", "integrity": "sha512-8IvaRvtFyzUnGGl3f5+1Cnor3LqaUWvhaUjAYO8Y39OUYlOf3cRd+dowuQYLpZcP3uwSG+mURwjEBOSq4SOJ0g==", "dev": true, "hasInstallScript": true, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "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, "license": "SEE LICENSE IN LICENSE.txt", "optional": true, "os": [ "win32" ] }, "node_modules/@vscode/vsce/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/@vscode/vsce/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/acorn": { "version": "8.16.0", "resolved": "https://registry.npmjs.org/acorn/-/acorn-8.16.0.tgz", "integrity": "sha512-UVJyE9MttOsBQIDKw1skb9nAwQuR5wuGD3+82K6JgJlm/Y+KI92oNsMNGZCYdDsVtRHSak0pcV5Dno5+4jh9sw==", "dev": true, "license": "MIT", "bin": { "acorn": "bin/acorn" }, "engines": { "node": ">=0.4.0" } }, "node_modules/acorn-jsx": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/acorn-jsx/-/acorn-jsx-5.3.2.tgz", "integrity": "sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==", "dev": true, "license": "MIT", "peerDependencies": { "acorn": "^6.0.0 || ^7.0.0 || ^8.0.0" } }, "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, "license": "MIT", "engines": { "node": ">= 14" } }, "node_modules/ajv": { "version": "6.14.0", "resolved": "https://registry.npmjs.org/ajv/-/ajv-6.14.0.tgz", "integrity": "sha512-IWrosm/yrn43eiKqkfkHis7QioDleaXQHdDVPKg0FSwwd/DuvyX79TZnFOnYpB7dcsFAMmtFztZuXPDvSePkFw==", "dev": true, "license": "MIT", "dependencies": { "fast-deep-equal": "^3.1.1", "fast-json-stable-stringify": "^2.0.0", "json-schema-traverse": "^0.4.1", "uri-js": "^4.2.2" }, "funding": { "type": "github", "url": "https://github.com/sponsors/epoberezkin" } }, "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, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/ansi-styles": { "version": "3.2.1", "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-3.2.1.tgz", "integrity": "sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==", "dev": true, "license": "MIT", "dependencies": { "color-convert": "^1.9.0" }, "engines": { "node": ">=4" } }, "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, "license": "Python-2.0" }, "node_modules/array-union": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/array-union/-/array-union-2.1.0.tgz", "integrity": "sha512-HGyxoOTYUyCM6stUe6EJgnd4EoewAI7zMdfqO+kGjnlZmBDz/cR5pf8r/cR4Wq60sL/p0IkcjUEEPwS3GFrIyw==", "dev": true, "license": "MIT", "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, "license": "MIT" }, "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, "license": "MIT", "dependencies": { "tunnel": "0.0.6", "typed-rest-client": "^1.8.4" } }, "node_modules/balanced-match": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==", "license": "MIT" }, "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" } ], "license": "MIT", "optional": true }, "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, "license": "MIT", "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, "license": "ISC" }, "node_modules/brace-expansion": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-2.0.2.tgz", "integrity": "sha512-Jt0vHyM+jmUBqojB7E1NIYadt0vI0Qxjxd2TErW94wDz+E2LAm5vKMXXwg6ZZBTHPuUlDgQHKXvjGBdfcF1ZDQ==", "license": "MIT", "dependencies": { "balanced-match": "^1.0.0" } }, "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, "license": "MIT", "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" } ], "license": "MIT", "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, "license": "MIT", "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, "license": "BSD-3-Clause" }, "node_modules/bundle-name": { "version": "4.1.0", "resolved": "https://registry.npmjs.org/bundle-name/-/bundle-name-4.1.0.tgz", "integrity": "sha512-tjwM5exMg6BGRI+kNmTntNsvdZS1X8BFYS6tnJ2hdH0kVxM6/eVZ2xy+FqStSWvYmtfFMDLIxurorHwDKfDz5Q==", "dev": true, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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/callsites": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/callsites/-/callsites-3.1.0.tgz", "integrity": "sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/chalk": { "version": "2.4.2", "resolved": "https://registry.npmjs.org/chalk/-/chalk-2.4.2.tgz", "integrity": "sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==", "dev": true, "license": "MIT", "dependencies": { "ansi-styles": "^3.2.1", "escape-string-regexp": "^1.0.5", "supports-color": "^5.3.0" }, "engines": { "node": ">=4" } }, "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, "license": "MIT", "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, "license": "BSD-2-Clause", "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, "license": "ISC", "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, "license": "MIT", "engines": { "node": ">=16" } }, "node_modules/color-convert": { "version": "1.9.3", "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-1.9.3.tgz", "integrity": "sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==", "dev": true, "license": "MIT", "dependencies": { "color-name": "1.1.3" } }, "node_modules/color-name": { "version": "1.1.3", "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.3.tgz", "integrity": "sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==", "dev": true, "license": "MIT" }, "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, "license": "MIT", "dependencies": { "delayed-stream": "~1.0.0" }, "engines": { "node": ">= 0.8" } }, "node_modules/commander": { "version": "6.2.1", "resolved": "https://registry.npmjs.org/commander/-/commander-6.2.1.tgz", "integrity": "sha512-U7VdrJFnJgo4xjrHpTzu0yrHPGImdsmD95ZlgYSEajAn2JKzDhDTPG9kBTefmObL2w/ngeZnilk+OV9CG3d7UA==", "dev": true, "license": "MIT", "engines": { "node": ">= 6" } }, "node_modules/concat-map": { "version": "0.0.1", "resolved": "https://registry.npmjs.org/concat-map/-/concat-map-0.0.1.tgz", "integrity": "sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==", "dev": true, "license": "MIT" }, "node_modules/cross-spawn": { "version": "7.0.6", "resolved": "https://registry.npmjs.org/cross-spawn/-/cross-spawn-7.0.6.tgz", "integrity": "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==", "dev": true, "license": "MIT", "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" }, "engines": { "node": ">= 8" } }, "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, "license": "BSD-2-Clause", "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, "license": "BSD-2-Clause", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "optional": true, "engines": { "node": ">=4.0.0" } }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", "integrity": "sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==", "dev": true, "license": "MIT" }, "node_modules/default-browser": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/default-browser/-/default-browser-5.5.0.tgz", "integrity": "sha512-H9LMLr5zwIbSxrmvikGuI/5KGhZ8E2zH3stkMgM5LpOWDutGM2JZaj460Udnf1a+946zc7YBgrqEWwbk7zHvGw==", "dev": true, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "Apache-2.0", "optional": true, "engines": { "node": ">=8" } }, "node_modules/dir-glob": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/dir-glob/-/dir-glob-3.0.1.tgz", "integrity": "sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==", "dev": true, "license": "MIT", "dependencies": { "path-type": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/doctrine": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/doctrine/-/doctrine-3.0.0.tgz", "integrity": "sha512-yS+Q5i3hBf7GBkd4KG8a7eBNNWNGLTaEwwYWUijIYM7zrlYDM0BFXHjjPWlWZ1Rg7UaddZeIDmi9jF3HmqiQ2w==", "dev": true, "license": "Apache-2.0", "dependencies": { "esutils": "^2.0.2" }, "engines": { "node": ">=6.0.0" } }, "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, "license": "MIT", "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" } ], "license": "BSD-2-Clause" }, "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, "license": "BSD-2-Clause", "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, "license": "BSD-2-Clause", "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, "license": "MIT", "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, "license": "Apache-2.0", "dependencies": { "safe-buffer": "^5.0.1" } }, "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, "license": "MIT", "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, "license": "MIT", "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, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "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, "license": "MIT", "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, "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/es-object-atoms": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/es-object-atoms/-/es-object-atoms-1.1.1.tgz", "integrity": "sha512-FGgH2h8zKNim9ljj7dankFPcICIK9Cp5bm+c2gQSYePhpaG5+esrLODihIorn+Pe6FGJzWhXQotPv73jTaldXA==", "dev": true, "license": "MIT", "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, "license": "MIT", "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/escape-string-regexp": { "version": "1.0.5", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-1.0.5.tgz", "integrity": "sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.0" } }, "node_modules/eslint": { "version": "8.57.1", "resolved": "https://registry.npmjs.org/eslint/-/eslint-8.57.1.tgz", "integrity": "sha512-ypowyDxpVSYpkXr9WPv2PAZCtNip1Mv5KTW0SCurXv/9iOpcrH9PaqUElksqEB6pChqHGDRCFTyrZlGhnLNGiA==", "deprecated": "This version is no longer supported. Please see https://eslint.org/version-support for other options.", "dev": true, "license": "MIT", "dependencies": { "@eslint-community/eslint-utils": "^4.2.0", "@eslint-community/regexpp": "^4.6.1", "@eslint/eslintrc": "^2.1.4", "@eslint/js": "8.57.1", "@humanwhocodes/config-array": "^0.13.0", "@humanwhocodes/module-importer": "^1.0.1", "@nodelib/fs.walk": "^1.2.8", "@ungap/structured-clone": "^1.2.0", "ajv": "^6.12.4", "chalk": "^4.0.0", "cross-spawn": "^7.0.2", "debug": "^4.3.2", "doctrine": "^3.0.0", "escape-string-regexp": "^4.0.0", "eslint-scope": "^7.2.2", "eslint-visitor-keys": "^3.4.3", "espree": "^9.6.1", "esquery": "^1.4.2", "esutils": "^2.0.2", "fast-deep-equal": "^3.1.3", "file-entry-cache": "^6.0.1", "find-up": "^5.0.0", "glob-parent": "^6.0.2", "globals": "^13.19.0", "graphemer": "^1.4.0", "ignore": "^5.2.0", "imurmurhash": "^0.1.4", "is-glob": "^4.0.0", "is-path-inside": "^3.0.3", "js-yaml": "^4.1.0", "json-stable-stringify-without-jsonify": "^1.0.1", "levn": "^0.4.1", "lodash.merge": "^4.6.2", "minimatch": "^3.1.2", "natural-compare": "^1.4.0", "optionator": "^0.9.3", "strip-ansi": "^6.0.1", "text-table": "^0.2.0" }, "bin": { "eslint": "bin/eslint.js" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-scope": { "version": "7.2.2", "resolved": "https://registry.npmjs.org/eslint-scope/-/eslint-scope-7.2.2.tgz", "integrity": "sha512-dOt21O7lTMhDM+X9mB4GX+DZrZtCUJPL/wlcTqxyrx5IvO0IYtILdtrQGQp+8n5S0gwSVmOf9NQrjMOgfQZlIg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "esrecurse": "^4.3.0", "estraverse": "^5.2.0" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint-visitor-keys": { "version": "3.4.3", "resolved": "https://registry.npmjs.org/eslint-visitor-keys/-/eslint-visitor-keys-3.4.3.tgz", "integrity": "sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==", "dev": true, "license": "Apache-2.0", "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/eslint/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, "license": "MIT", "dependencies": { "color-convert": "^2.0.1" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/chalk/ansi-styles?sponsor=1" } }, "node_modules/eslint/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/eslint/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, "license": "MIT", "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/eslint/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, "license": "MIT", "dependencies": { "color-name": "~1.1.4" }, "engines": { "node": ">=7.0.0" } }, "node_modules/eslint/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, "license": "MIT" }, "node_modules/eslint/node_modules/escape-string-regexp": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/escape-string-regexp/-/escape-string-regexp-4.0.0.tgz", "integrity": "sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/eslint/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, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/eslint/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/eslint/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, "license": "MIT", "dependencies": { "has-flag": "^4.0.0" }, "engines": { "node": ">=8" } }, "node_modules/espree": { "version": "9.6.1", "resolved": "https://registry.npmjs.org/espree/-/espree-9.6.1.tgz", "integrity": "sha512-oruZaFkjorTpF32kDSI5/75ViwGeZginGGy2NoOSg3Q9bnwlnmDm4HLnkl0RE3n+njDXR037aY1+x58Z/zFdwQ==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "acorn": "^8.9.0", "acorn-jsx": "^5.3.2", "eslint-visitor-keys": "^3.4.1" }, "engines": { "node": "^12.22.0 || ^14.17.0 || >=16.0.0" }, "funding": { "url": "https://opencollective.com/eslint" } }, "node_modules/esquery": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/esquery/-/esquery-1.7.0.tgz", "integrity": "sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "estraverse": "^5.1.0" }, "engines": { "node": ">=0.10" } }, "node_modules/esrecurse": { "version": "4.3.0", "resolved": "https://registry.npmjs.org/esrecurse/-/esrecurse-4.3.0.tgz", "integrity": "sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "estraverse": "^5.2.0" }, "engines": { "node": ">=4.0" } }, "node_modules/estraverse": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/estraverse/-/estraverse-5.3.0.tgz", "integrity": "sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=4.0" } }, "node_modules/esutils": { "version": "2.0.3", "resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.3.tgz", "integrity": "sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==", "dev": true, "license": "BSD-2-Clause", "engines": { "node": ">=0.10.0" } }, "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, "license": "(MIT OR WTFPL)", "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, "license": "MIT" }, "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, "license": "MIT", "dependencies": { "@nodelib/fs.stat": "^2.0.2", "@nodelib/fs.walk": "^1.2.3", "glob-parent": "^5.1.2", "merge2": "^1.3.0", "micromatch": "^4.0.8" }, "engines": { "node": ">=8.6.0" } }, "node_modules/fast-glob/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, "license": "ISC", "dependencies": { "is-glob": "^4.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/fast-json-stable-stringify": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/fast-json-stable-stringify/-/fast-json-stable-stringify-2.1.0.tgz", "integrity": "sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==", "dev": true, "license": "MIT" }, "node_modules/fast-levenshtein": { "version": "2.0.6", "resolved": "https://registry.npmjs.org/fast-levenshtein/-/fast-levenshtein-2.0.6.tgz", "integrity": "sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==", "dev": true, "license": "MIT" }, "node_modules/fastq": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/fastq/-/fastq-1.20.1.tgz", "integrity": "sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==", "dev": true, "license": "ISC", "dependencies": { "reusify": "^1.0.4" } }, "node_modules/fd-slicer": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/fd-slicer/-/fd-slicer-1.1.0.tgz", "integrity": "sha512-cE1qsB/VwyQozZ+q1dGxR8LBYNZeofhEdUNGSMbQD3Gw2lAzX9Zb3uIU6Ebc/Fmyjo9AWWfnn0AUCHqtevs/8g==", "dev": true, "license": "MIT", "dependencies": { "pend": "~1.2.0" } }, "node_modules/file-entry-cache": { "version": "6.0.1", "resolved": "https://registry.npmjs.org/file-entry-cache/-/file-entry-cache-6.0.1.tgz", "integrity": "sha512-7Gps/XWymbLk2QLYK4NzpMOrYjMhdIxXuIvy2QBsLE6ljuodKvdkWs/cpyJJ3CVIVpH0Oi1Hvg1ovbMzLdFBBg==", "dev": true, "license": "MIT", "dependencies": { "flat-cache": "^3.0.4" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "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, "license": "MIT", "dependencies": { "to-regex-range": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/find-up": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/find-up/-/find-up-5.0.0.tgz", "integrity": "sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==", "dev": true, "license": "MIT", "dependencies": { "locate-path": "^6.0.0", "path-exists": "^4.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/flat-cache": { "version": "3.2.0", "resolved": "https://registry.npmjs.org/flat-cache/-/flat-cache-3.2.0.tgz", "integrity": "sha512-CYcENa+FtcUKLmhhqyctpclsq7QF38pKjZHsGNiSQF5r4FtoKDWabFDl3hzaEQMvT1LHEysw5twgLvpYYb4vbw==", "dev": true, "license": "MIT", "dependencies": { "flatted": "^3.2.9", "keyv": "^4.5.3", "rimraf": "^3.0.2" }, "engines": { "node": "^10.12.0 || >=12.0.0" } }, "node_modules/flatted": { "version": "3.3.4", "resolved": "https://registry.npmjs.org/flatted/-/flatted-3.3.4.tgz", "integrity": "sha512-3+mMldrTAPdta5kjX2G2J7iX4zxtnwpdA8Tr2ZSjkyPSanvbZAcy6flmtnXbEybHrDcU9641lxrMfFuUxVz9vA==", "dev": true, "license": "ISC" }, "node_modules/form-data": { "version": "4.0.5", "resolved": "https://registry.npmjs.org/form-data/-/form-data-4.0.5.tgz", "integrity": "sha512-8RipRLol37bNs2bhoV67fiTEvdTrbMUYcFTiy3+wuuOnUog2QBHCZWXDRijWQfAkhBj2Uf5UnVaiWwA5vdd82w==", "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", "combined-stream": "^1.0.8", "es-set-tostringtag": "^2.1.0", "hasown": "^2.0.2", "mime-types": "^2.1.12" }, "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, "license": "MIT", "optional": true }, "node_modules/fs.realpath": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/fs.realpath/-/fs.realpath-1.0.0.tgz", "integrity": "sha512-OO0pH2lK6a0hZnAdau5ItzHPI6pUlvI7jMVnxUQRtw4owF2wk8lOSabtGDCTP4Ggrg2MbGnWO9X8K1t4+fGMDw==", "dev": true, "license": "ISC" }, "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "optional": true }, "node_modules/glob": { "version": "7.2.3", "resolved": "https://registry.npmjs.org/glob/-/glob-7.2.3.tgz", "integrity": "sha512-nFR0zLpU2YCaRxwoCJvL6UvCH2JFyFVIvwTLsIf21AuHlMskA1hhTdk+LlYJtOlYt9v6dvszD2BGRqBL+iQK9Q==", "deprecated": "Old versions of glob are not supported, and contain widely publicized security vulnerabilities, which have been fixed in the current version. Please update. Support for old versions may be purchased (at exorbitant rates) by contacting i@izs.me", "dev": true, "license": "ISC", "dependencies": { "fs.realpath": "^1.0.0", "inflight": "^1.0.4", "inherits": "2", "minimatch": "^3.1.1", "once": "^1.3.0", "path-is-absolute": "^1.0.0" }, "engines": { "node": "*" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "node_modules/glob-parent": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/glob-parent/-/glob-parent-6.0.2.tgz", "integrity": "sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==", "dev": true, "license": "ISC", "dependencies": { "is-glob": "^4.0.3" }, "engines": { "node": ">=10.13.0" } }, "node_modules/glob/node_modules/brace-expansion": { "version": "1.1.12", "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.12.tgz", "integrity": "sha512-9T9UjW3r0UW5c1Q7GTwllptXwhvYmEzFhzMfZ9H7FQWt+uZePjZPjBP/W1ZEyZ1twGWom5/56TF4lPcqjnDHcg==", "dev": true, "license": "MIT", "dependencies": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" } }, "node_modules/glob/node_modules/minimatch": { "version": "3.1.5", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.5.tgz", "integrity": "sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^1.1.7" }, "engines": { "node": "*" } }, "node_modules/globals": { "version": "13.24.0", "resolved": "https://registry.npmjs.org/globals/-/globals-13.24.0.tgz", "integrity": "sha512-AhO5QUcj8llrbG09iWhPU2B204J1xnPeL8kQmVorSsy+Sjj1sk8gIyh6cUocGmH4L0UuhAJy+hJMRA4mgA4mFQ==", "dev": true, "license": "MIT", "dependencies": { "type-fest": "^0.20.2" }, "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/globby": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/globby/-/globby-11.1.0.tgz", "integrity": "sha512-jhIXaOzy1sb8IyocaruWSn1TjmnBVs8Ayhcy83rmxNJ8q2uWKCAj3CnJY+KpGSXCueAPc0i05kVvVKtP1t9S3g==", "dev": true, "license": "MIT", "dependencies": { "array-union": "^2.1.0", "dir-glob": "^3.0.1", "fast-glob": "^3.2.9", "ignore": "^5.2.0", "merge2": "^1.4.1", "slash": "^3.0.0" }, "engines": { "node": ">=10" }, "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, "license": "MIT", "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/graphemer": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/graphemer/-/graphemer-1.4.0.tgz", "integrity": "sha512-EtKwoO6kxCL9WO5xipiHTZlSzBm7WLT627TqC/uVRd0HKmq8NXyebnNYxDoBi7wt8eTWrUrKXCOVaFq9x1kgag==", "dev": true, "license": "MIT" }, "node_modules/has-flag": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-3.0.0.tgz", "integrity": "sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "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, "license": "MIT", "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, "license": "MIT", "dependencies": { "has-symbols": "^1.0.3" }, "engines": { "node": ">= 0.4" }, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "node_modules/hasown": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.2.tgz", "integrity": "sha512-0hJU9SCPvmMzIBdZFqNPXWa6dqh7WdH0cII9y+CyS8rG3nL48Bclra9HmKhVVUHyPWNH5Y7xDwAB7bfgSjkUMQ==", "dev": true, "license": "MIT", "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, "license": "ISC", "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" } ], "license": "MIT", "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, "license": "BSD-2-Clause", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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" } ], "license": "BSD-3-Clause", "optional": true }, "node_modules/ignore": { "version": "5.3.2", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.2.tgz", "integrity": "sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==", "dev": true, "license": "MIT", "engines": { "node": ">= 4" } }, "node_modules/import-fresh": { "version": "3.3.1", "resolved": "https://registry.npmjs.org/import-fresh/-/import-fresh-3.3.1.tgz", "integrity": "sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==", "dev": true, "license": "MIT", "dependencies": { "parent-module": "^1.0.0", "resolve-from": "^4.0.0" }, "engines": { "node": ">=6" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/imurmurhash": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/imurmurhash/-/imurmurhash-0.1.4.tgz", "integrity": "sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.8.19" } }, "node_modules/inflight": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/inflight/-/inflight-1.0.6.tgz", "integrity": "sha512-k92I/b08q4wvFscXCLvqfsHCrjrF7yiXsQuIVvVE7N82W3+aqpzuUdBbfhWcy/FZR3/4IgflMgKLOsvPDrGCJA==", "deprecated": "This module is not supported, and leaks memory. Do not use it. Check out lru-cache if you want a good and tested way to coalesce async requests by a key value, which is much more comprehensive and powerful.", "dev": true, "license": "ISC", "dependencies": { "once": "^1.3.0", "wrappy": "1" } }, "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, "license": "ISC" }, "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, "license": "ISC", "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, "license": "MIT", "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, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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, "license": "MIT", "dependencies": { "is-extglob": "^2.1.1" }, "engines": { "node": ">=0.10.0" } }, "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, "license": "MIT", "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, "license": "MIT", "engines": { "node": ">=0.12.0" } }, "node_modules/is-path-inside": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/is-path-inside/-/is-path-inside-3.0.3.tgz", "integrity": "sha512-Fd4gABb+ycGAmKou8eMftCupSir5lRxqf4aD/vd0cD2qc4HL07OjCeuHMr8Ro4CoMaeCKDB0/ECBOVWjTwUvPQ==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "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, "license": "MIT", "dependencies": { "is-inside-container": "^1.0.0" }, "engines": { "node": ">=16" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/isexe": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/isexe/-/isexe-2.0.0.tgz", "integrity": "sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==", "dev": true, "license": "ISC" }, "node_modules/js-yaml": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/js-yaml/-/js-yaml-4.1.1.tgz", "integrity": "sha512-qQKT4zQxXl8lLwBtHMWwaTcGfFOZviOJet3Oy/xmGk2gZH677CJM9EvtfdSkgWcATZhj/55JZ0rmy3myCT5lsA==", "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1" }, "bin": { "js-yaml": "bin/js-yaml.js" } }, "node_modules/json-buffer": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==", "dev": true, "license": "MIT" }, "node_modules/json-schema-traverse": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-0.4.1.tgz", "integrity": "sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==", "dev": true, "license": "MIT" }, "node_modules/json-stable-stringify-without-jsonify": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/json-stable-stringify-without-jsonify/-/json-stable-stringify-without-jsonify-1.0.1.tgz", "integrity": "sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==", "dev": true, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "optional": true, "dependencies": { "node-addon-api": "^4.3.0", "prebuild-install": "^7.0.1" } }, "node_modules/keyv": { "version": "4.5.4", "resolved": "https://registry.npmjs.org/keyv/-/keyv-4.5.4.tgz", "integrity": "sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==", "dev": true, "license": "MIT", "dependencies": { "json-buffer": "3.0.1" } }, "node_modules/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, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/levn": { "version": "0.4.1", "resolved": "https://registry.npmjs.org/levn/-/levn-0.4.1.tgz", "integrity": "sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1", "type-check": "~0.4.0" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/linkify-it": { "version": "3.0.3", "resolved": "https://registry.npmjs.org/linkify-it/-/linkify-it-3.0.3.tgz", "integrity": "sha512-ynTsyrFSdE5oZ/O9GEf00kPngmOfVwazR5GKDq6EYfhlpFug3J2zybX56a2PRRpc9P+FuSoGNAwjlbDs9jJBPQ==", "dev": true, "license": "MIT", "dependencies": { "uc.micro": "^1.0.1" } }, "node_modules/locate-path": { "version": "6.0.0", "resolved": "https://registry.npmjs.org/locate-path/-/locate-path-6.0.0.tgz", "integrity": "sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==", "dev": true, "license": "MIT", "dependencies": { "p-locate": "^5.0.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "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, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "MIT" }, "node_modules/lodash.merge": { "version": "4.6.2", "resolved": "https://registry.npmjs.org/lodash.merge/-/lodash.merge-4.6.2.tgz", "integrity": "sha512-0KpjqXRVvrYyCsX1swR/XTK0va6VQkQM6MNo7PqW77ByjAhoARA8EfrP1N4+KlKj8YS0ZUCtRT/YUuhyYDujIQ==", "dev": true, "license": "MIT" }, "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, "license": "MIT" }, "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, "license": "ISC", "dependencies": { "yallist": "^4.0.0" }, "engines": { "node": ">=10" } }, "node_modules/markdown-it": { "version": "12.3.2", "resolved": "https://registry.npmjs.org/markdown-it/-/markdown-it-12.3.2.tgz", "integrity": "sha512-TchMembfxfNVpHkbtriWltGWc+m3xszaRD0CZup7GFFhzIgQqxIfn3eGj1yZpfuflzPvfkt611B2Q/Bsk1YnGg==", "dev": true, "license": "MIT", "dependencies": { "argparse": "^2.0.1", "entities": "~2.1.0", "linkify-it": "^3.0.1", "mdurl": "^1.0.1", "uc.micro": "^1.0.5" }, "bin": { "markdown-it": "bin/markdown-it.js" } }, "node_modules/markdown-it/node_modules/entities": { "version": "2.1.0", "resolved": "https://registry.npmjs.org/entities/-/entities-2.1.0.tgz", "integrity": "sha512-hCx1oky9PFrJ611mf0ifBLBRW8lUUVRlFolb5gWRfIELabBlbp9xZvrqZLZAs+NxFnbfQoeGd8wDkygjg7U85w==", "dev": true, "license": "BSD-2-Clause", "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "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, "license": "MIT", "engines": { "node": ">= 0.4" } }, "node_modules/mdurl": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/mdurl/-/mdurl-1.0.1.tgz", "integrity": "sha512-/sKlQJCBYVY9Ers9hqzKou4H6V5UWc/M59TH2dvkt+84itfnq7uFOMLpOiOS4ujvHP4etln18fmIxA5R5fll0g==", "dev": true, "license": "MIT" }, "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "optional": true, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/minimatch": { "version": "9.0.3", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-9.0.3.tgz", "integrity": "sha512-RHiac9mvaRw0x3AYRgDC1CxAP7HTcNrrECeA8YYJeWnpo+2Q5CegtZjaotWTWxDG3UeGA1coE05iH1mPjT/2mg==", "dev": true, "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=16 || 14 >=14.17" }, "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, "license": "MIT", "optional": true, "funding": { "url": "https://github.com/sponsors/ljharb" } }, "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, "license": "MIT", "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, "license": "MIT" }, "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, "license": "ISC" }, "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, "license": "MIT", "optional": true }, "node_modules/natural-compare": { "version": "1.4.0", "resolved": "https://registry.npmjs.org/natural-compare/-/natural-compare-1.4.0.tgz", "integrity": "sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==", "dev": true, "license": "MIT" }, "node_modules/node-abi": { "version": "3.87.0", "resolved": "https://registry.npmjs.org/node-abi/-/node-abi-3.87.0.tgz", "integrity": "sha512-+CGM1L1CgmtheLcBuleyYOn7NWPVu0s0EJH2C4puxgEZb9h8QpR9G2dBfZJOAUhi7VQxuBPMd0hiISWcTyiYyQ==", "dev": true, "license": "MIT", "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, "license": "MIT", "optional": 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, "license": "BSD-2-Clause", "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, "license": "MIT", "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, "license": "ISC", "dependencies": { "wrappy": "1" } }, "node_modules/open": { "version": "10.2.0", "resolved": "https://registry.npmjs.org/open/-/open-10.2.0.tgz", "integrity": "sha512-YgBpdJHPyQ2UE5x+hlSXcnejzAvD0b22U2OuAP+8OnlJT+PjWPxtgmGqKKc+RgTM63U9gN0YzrYc71R2WT/hTA==", "dev": true, "license": "MIT", "dependencies": { "default-browser": "^5.2.1", "define-lazy-prop": "^3.0.0", "is-inside-container": "^1.0.0", "wsl-utils": "^0.1.0" }, "engines": { "node": ">=18" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/optionator": { "version": "0.9.4", "resolved": "https://registry.npmjs.org/optionator/-/optionator-0.9.4.tgz", "integrity": "sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==", "dev": true, "license": "MIT", "dependencies": { "deep-is": "^0.1.3", "fast-levenshtein": "^2.0.6", "levn": "^0.4.1", "prelude-ls": "^1.2.1", "type-check": "^0.4.0", "word-wrap": "^1.2.5" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/p-limit": { "version": "3.1.0", "resolved": "https://registry.npmjs.org/p-limit/-/p-limit-3.1.0.tgz", "integrity": "sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==", "dev": true, "license": "MIT", "dependencies": { "yocto-queue": "^0.1.0" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/p-locate": { "version": "5.0.0", "resolved": "https://registry.npmjs.org/p-locate/-/p-locate-5.0.0.tgz", "integrity": "sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==", "dev": true, "license": "MIT", "dependencies": { "p-limit": "^3.0.2" }, "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/parent-module": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/parent-module/-/parent-module-1.0.1.tgz", "integrity": "sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==", "dev": true, "license": "MIT", "dependencies": { "callsites": "^3.0.0" }, "engines": { "node": ">=6" } }, "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, "license": "MIT", "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, "license": "ISC", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "MIT", "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, "license": "BSD-2-Clause", "engines": { "node": ">=0.12" }, "funding": { "url": "https://github.com/fb55/entities?sponsor=1" } }, "node_modules/path-exists": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-exists/-/path-exists-4.0.0.tgz", "integrity": "sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-is-absolute": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/path-is-absolute/-/path-is-absolute-1.0.1.tgz", "integrity": "sha512-AVbw3UJ2e9bq64vSaS9Am0fje1Pa8pbGqTTsmXfaIiMpnr5DlDhfJOuLj9Sf95ZPVDAUerDfEk88MPmPe7UCQg==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "node_modules/path-key": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/path-key/-/path-key-3.1.1.tgz", "integrity": "sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/path-type": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/path-type/-/path-type-4.0.0.tgz", "integrity": "sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "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, "license": "MIT" }, "node_modules/picomatch": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/picomatch/-/picomatch-2.3.1.tgz", "integrity": "sha512-JU3teHTNjmE2VCGFzuY8EXzCDVwEqB2a8fsIvwaStHhAWJEeVd1o1QD80CU6+ZdEXXSLbSsuLwJjkCBWqRQUVA==", "dev": true, "license": "MIT", "engines": { "node": ">=8.6" }, "funding": { "url": "https://github.com/sponsors/jonschlinkert" } }, "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, "license": "MIT", "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/prelude-ls": { "version": "1.2.1", "resolved": "https://registry.npmjs.org/prelude-ls/-/prelude-ls-1.2.1.tgz", "integrity": "sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==", "dev": true, "license": "MIT", "engines": { "node": ">= 0.8.0" } }, "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, "license": "MIT", "optional": true, "dependencies": { "end-of-stream": "^1.1.0", "once": "^1.3.1" } }, "node_modules/punycode": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/punycode/-/punycode-2.3.1.tgz", "integrity": "sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==", "dev": true, "license": "MIT", "engines": { "node": ">=6" } }, "node_modules/qs": { "version": "6.15.0", "resolved": "https://registry.npmjs.org/qs/-/qs-6.15.0.tgz", "integrity": "sha512-mAZTtNCeetKMH+pSjrb76NAM8V9a05I9aBZOHztWy/UqcJdQYNsf59vrRKWnojAT9Y+GbIvoTBC++CPHqpDBhQ==", "dev": true, "license": "BSD-3-Clause", "dependencies": { "side-channel": "^1.1.0" }, "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" } ], "license": "MIT" }, "node_modules/rc": { "version": "1.2.8", "resolved": "https://registry.npmjs.org/rc/-/rc-1.2.8.tgz", "integrity": "sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==", "dev": true, "license": "(BSD-2-Clause OR MIT OR Apache-2.0)", "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/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, "license": "MIT", "optional": true, "engines": { "node": ">=0.10.0" } }, "node_modules/read": { "version": "1.0.7", "resolved": "https://registry.npmjs.org/read/-/read-1.0.7.tgz", "integrity": "sha512-rSOKNYUmaxy0om1BNjMN4ezNT6VKK+2xF4GBhc81mkH7L60i6dp8qPYrkndNLT3QPphoII3maL9PVC9XmhHwVQ==", "dev": true, "license": "ISC", "dependencies": { "mute-stream": "~0.0.4" }, "engines": { "node": ">=0.8" } }, "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, "license": "MIT", "optional": true, "dependencies": { "inherits": "^2.0.3", "string_decoder": "^1.1.1", "util-deprecate": "^1.0.1" }, "engines": { "node": ">= 6" } }, "node_modules/resolve-from": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/resolve-from/-/resolve-from-4.0.0.tgz", "integrity": "sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==", "dev": true, "license": "MIT", "engines": { "node": ">=4" } }, "node_modules/reusify": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/reusify/-/reusify-1.1.0.tgz", "integrity": "sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==", "dev": true, "license": "MIT", "engines": { "iojs": ">=1.0.0", "node": ">=0.10.0" } }, "node_modules/rimraf": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/rimraf/-/rimraf-3.0.2.tgz", "integrity": "sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==", "deprecated": "Rimraf versions prior to v4 are no longer supported", "dev": true, "license": "ISC", "dependencies": { "glob": "^7.1.3" }, "bin": { "rimraf": "bin.js" }, "funding": { "url": "https://github.com/sponsors/isaacs" } }, "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, "license": "MIT", "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" } ], "license": "MIT", "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" } ], "license": "MIT" }, "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, "license": "MIT" }, "node_modules/sax": { "version": "1.5.0", "resolved": "https://registry.npmjs.org/sax/-/sax-1.5.0.tgz", "integrity": "sha512-21IYA3Q5cQf089Z6tgaUTr7lDAyzoTPx5HRtbhsME8Udispad8dC/+sziTNugOEx54ilvatQ9YCzl4KQLPcRHA==", "dev": true, "license": "BlueOak-1.0.0", "engines": { "node": ">=11.0.0" } }, "node_modules/semver": { "version": "7.7.4", "resolved": "https://registry.npmjs.org/semver/-/semver-7.7.4.tgz", "integrity": "sha512-vFKC2IEtQnVhpT78h1Yp8wzwrf8CM+MzKMHGJZfBtzhZNycRFnXsHk6E5TxIkkMsgNS7mdX3AGB7x2QM2di4lA==", "license": "ISC", "bin": { "semver": "bin/semver.js" }, "engines": { "node": ">=10" } }, "node_modules/shebang-command": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/shebang-command/-/shebang-command-2.0.0.tgz", "integrity": "sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==", "dev": true, "license": "MIT", "dependencies": { "shebang-regex": "^3.0.0" }, "engines": { "node": ">=8" } }, "node_modules/shebang-regex": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/shebang-regex/-/shebang-regex-3.0.0.tgz", "integrity": "sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "node_modules/side-channel": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/side-channel/-/side-channel-1.1.0.tgz", "integrity": "sha512-ZX99e6tRweoUXqR+VBrslhda51Nh5MTQwou5tnUDgbtyM0dBgmhEDtWGP/xbKn6hqfPRHujUNwz5fy/wbbhnpw==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3", "side-channel-list": "^1.0.0", "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.0", "resolved": "https://registry.npmjs.org/side-channel-list/-/side-channel-list-1.0.0.tgz", "integrity": "sha512-FCLHtRD/gnpCiCHEiJLOwdmFP+wzCmDEkc9y7NsYxeF4u7Btsn1ZuwgwJGxImImHicJArLP4R0yX4c2KCrMrTA==", "dev": true, "license": "MIT", "dependencies": { "es-errors": "^1.3.0", "object-inspect": "^1.13.3" }, "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, "license": "MIT", "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, "license": "MIT", "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" } ], "license": "MIT", "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" } ], "license": "MIT", "optional": true, "dependencies": { "decompress-response": "^6.0.0", "once": "^1.3.1", "simple-concat": "^1.0.0" } }, "node_modules/slash": { "version": "3.0.0", "resolved": "https://registry.npmjs.org/slash/-/slash-3.0.0.tgz", "integrity": "sha512-g9Q1haeby36OSStwb4ntCGGGaKsaVSjQ68fBxoQcutl5fS1vuY18H3wSt3jFyFtrkx+Kz0V1G85A4MyAdDMi2Q==", "dev": true, "license": "MIT", "engines": { "node": ">=8" } }, "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, "license": "MIT", "optional": true, "dependencies": { "safe-buffer": "~5.2.0" } }, "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, "license": "MIT", "dependencies": { "ansi-regex": "^5.0.1" }, "engines": { "node": ">=8" } }, "node_modules/strip-json-comments": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/strip-json-comments/-/strip-json-comments-3.1.1.tgz", "integrity": "sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==", "dev": true, "license": "MIT", "engines": { "node": ">=8" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } }, "node_modules/supports-color": { "version": "5.5.0", "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-5.5.0.tgz", "integrity": "sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==", "dev": true, "license": "MIT", "dependencies": { "has-flag": "^3.0.0" }, "engines": { "node": ">=4" } }, "node_modules/tar-fs": { "version": "2.1.4", "resolved": "https://registry.npmjs.org/tar-fs/-/tar-fs-2.1.4.tgz", "integrity": "sha512-mDAjwmZdh7LTT6pNleZ05Yt65HC3E+NiQzl672vQG38jIrehtJk/J3mNwIg+vShQPcLF/LV7CMnDW6vjj6sfYQ==", "dev": true, "license": "MIT", "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, "license": "MIT", "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/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, "license": "MIT" }, "node_modules/tmp": { "version": "0.2.5", "resolved": "https://registry.npmjs.org/tmp/-/tmp-0.2.5.tgz", "integrity": "sha512-voyz6MApa1rQGUxT3E+BK7/ROe8itEx7vD8/HEvt4xwXucvQ5G5oeEiHkmHZJuBO21RpOf+YYm9MOivj709jow==", "dev": true, "license": "MIT", "engines": { "node": ">=14.14" } }, "node_modules/to-regex-range": { "version": "5.0.1", "resolved": "https://registry.npmjs.org/to-regex-range/-/to-regex-range-5.0.1.tgz", "integrity": "sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==", "dev": true, "license": "MIT", "dependencies": { "is-number": "^7.0.0" }, "engines": { "node": ">=8.0" } }, "node_modules/ts-api-utils": { "version": "1.4.3", "resolved": "https://registry.npmjs.org/ts-api-utils/-/ts-api-utils-1.4.3.tgz", "integrity": "sha512-i3eMG77UTMD0hZhgRS562pv83RC6ukSAC2GMNWc+9dieh/+jDM5u5YG+NHX6VNDRHQcHwmsTHctP9LhbC3WxVw==", "dev": true, "license": "MIT", "engines": { "node": ">=16" }, "peerDependencies": { "typescript": ">=4.2.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, "license": "0BSD" }, "node_modules/tunnel": { "version": "0.0.6", "resolved": "https://registry.npmjs.org/tunnel/-/tunnel-0.0.6.tgz", "integrity": "sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==", "dev": true, "license": "MIT", "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, "license": "Apache-2.0", "optional": true, "dependencies": { "safe-buffer": "^5.0.1" }, "engines": { "node": "*" } }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", "integrity": "sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==", "dev": true, "license": "MIT", "dependencies": { "prelude-ls": "^1.2.1" }, "engines": { "node": ">= 0.8.0" } }, "node_modules/type-fest": { "version": "0.20.2", "resolved": "https://registry.npmjs.org/type-fest/-/type-fest-0.20.2.tgz", "integrity": "sha512-Ne+eE4r0/iWnpAxD852z3A+N0Bt5RN//NjJwRd2VFHEmrywxf5vsZlh4R6lixl6B+wz/8d+maTSAkN1FIkI3LQ==", "dev": true, "license": "(MIT OR CC0-1.0)", "engines": { "node": ">=10" }, "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, "license": "MIT", "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, "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" }, "engines": { "node": ">=14.17" } }, "node_modules/uc.micro": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/uc.micro/-/uc.micro-1.0.6.tgz", "integrity": "sha512-8Y75pvTYkLJW2hWQHXxoqRgV7qb9B+9vFEtidML+7koHUFapnVJAZ6cKs+Qjz5Aw3aZWHMC6u0wJE3At+nSGwA==", "dev": true, "license": "MIT" }, "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, "license": "MIT" }, "node_modules/undici": { "version": "7.22.0", "resolved": "https://registry.npmjs.org/undici/-/undici-7.22.0.tgz", "integrity": "sha512-RqslV2Us5BrllB+JeiZnK4peryVTndy9Dnqq62S3yYRRTj0tFQCwEniUy2167skdGOy3vqRzEvl1Dm4sV2ReDg==", "dev": true, "license": "MIT", "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, "license": "MIT" }, "node_modules/uri-js": { "version": "4.4.1", "resolved": "https://registry.npmjs.org/uri-js/-/uri-js-4.4.1.tgz", "integrity": "sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==", "dev": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.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, "license": "MIT" }, "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, "license": "MIT", "optional": true }, "node_modules/uuid": { "version": "8.3.2", "resolved": "https://registry.npmjs.org/uuid/-/uuid-8.3.2.tgz", "integrity": "sha512-+NYs2QeMWy+GWFOEm9xnn6HCDp0l7QBD7ml8zLUmJ+93Q5NF0NocErnwkTkXVFNiX3/fpC6afS8Dhb/gz7R7eg==", "dev": true, "license": "MIT", "bin": { "uuid": "dist/bin/uuid" } }, "node_modules/vscode-jsonrpc": { "version": "8.2.0", "resolved": "https://registry.npmjs.org/vscode-jsonrpc/-/vscode-jsonrpc-8.2.0.tgz", "integrity": "sha512-C+r0eKJUIfiDIfwJhria30+TYWPtuHJXHtI7J0YlOmKAo7ogxP20T0zxB7HZQIFhIyvoBPwWskjxrvAtfjyZfA==", "license": "MIT", "engines": { "node": ">=14.0.0" } }, "node_modules/vscode-languageclient": { "version": "9.0.1", "resolved": "https://registry.npmjs.org/vscode-languageclient/-/vscode-languageclient-9.0.1.tgz", "integrity": "sha512-JZiimVdvimEuHh5olxhxkht09m3JzUGwggb5eRUkzzJhZ2KjCN0nh55VfiED9oez9DyF8/fz1g1iBV3h+0Z2EA==", "license": "MIT", "dependencies": { "minimatch": "^5.1.0", "semver": "^7.3.7", "vscode-languageserver-protocol": "3.17.5" }, "engines": { "vscode": "^1.82.0" } }, "node_modules/vscode-languageclient/node_modules/minimatch": { "version": "5.1.9", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-5.1.9.tgz", "integrity": "sha512-7o1wEA2RyMP7Iu7GNba9vc0RWWGACJOCZBJX2GJWip0ikV+wcOsgVuY9uE8CPiyQhkGFSlhuSkZPavN7u1c2Fw==", "license": "ISC", "dependencies": { "brace-expansion": "^2.0.1" }, "engines": { "node": ">=10" } }, "node_modules/vscode-languageserver-protocol": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-protocol/-/vscode-languageserver-protocol-3.17.5.tgz", "integrity": "sha512-mb1bvRJN8SVznADSGWM9u/b07H7Ecg0I3OgXDuLdn307rl/J3A9YD6/eYOssqhecL27hK1IPZAsaqh00i/Jljg==", "license": "MIT", "dependencies": { "vscode-jsonrpc": "8.2.0", "vscode-languageserver-types": "3.17.5" } }, "node_modules/vscode-languageserver-types": { "version": "3.17.5", "resolved": "https://registry.npmjs.org/vscode-languageserver-types/-/vscode-languageserver-types-3.17.5.tgz", "integrity": "sha512-Ld1VelNuX9pdF39h2Hgaeb5hEZM2Z3jUrrMgWQAu82jMtZp7p3vJT3BzToKtZI7NgQssZje5o0zryOrhQvzQAg==", "license": "MIT" }, "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, "license": "MIT", "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, "license": "MIT", "engines": { "node": ">=18" } }, "node_modules/which": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/which/-/which-2.0.2.tgz", "integrity": "sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==", "dev": true, "license": "ISC", "dependencies": { "isexe": "^2.0.0" }, "bin": { "node-which": "bin/node-which" }, "engines": { "node": ">= 8" } }, "node_modules/word-wrap": { "version": "1.2.5", "resolved": "https://registry.npmjs.org/word-wrap/-/word-wrap-1.2.5.tgz", "integrity": "sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==", "dev": true, "license": "MIT", "engines": { "node": ">=0.10.0" } }, "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, "license": "ISC" }, "node_modules/wsl-utils": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/wsl-utils/-/wsl-utils-0.1.0.tgz", "integrity": "sha512-h3Fbisa2nKGPxCpm89Hk33lBLsnaGBvctQopaBSOW/uIs6FTe1ATyAnKFJrzVs9vpGdsTe73WF3V4lIsk4Gacw==", "dev": true, "license": "MIT", "dependencies": { "is-wsl": "^3.1.0" }, "engines": { "node": ">=18" }, "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, "license": "MIT", "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, "license": "MIT", "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, "license": "ISC" }, "node_modules/yauzl": { "version": "2.10.0", "resolved": "https://registry.npmjs.org/yauzl/-/yauzl-2.10.0.tgz", "integrity": "sha512-p4a9I6X6nu6IhoGmBqAcbJy1mlC4j27vEPZX9F4L4/vZT3Lyq1VkFHw/V/PUcB9Buo+DG3iHkT0x3Qya58zc3g==", "dev": true, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3", "fd-slicer": "~1.1.0" } }, "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, "license": "MIT", "dependencies": { "buffer-crc32": "~0.2.3" } }, "node_modules/yocto-queue": { "version": "0.1.0", "resolved": "https://registry.npmjs.org/yocto-queue/-/yocto-queue-0.1.0.tgz", "integrity": "sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==", "dev": true, "license": "MIT", "engines": { "node": ">=10" }, "funding": { "url": "https://github.com/sponsors/sindresorhus" } } } } debian-lsp-0.1.4/vscode-debian/package.json000064400000000000000000000166671046102023000166550ustar 00000000000000{ "name": "vscode-debian", "displayName": "Debian Language Support", "description": "Language Server Protocol support for Debian packaging files", "version": "0.1.0", "publisher": "debian", "license": "Apache-2.0+", "icon": "images/debian-logo-256.png", "repository": { "type": "git", "url": "https://github.com/jelmer/debian-lsp" }, "bugs": { "url": "https://github.com/jelmer/debian-lsp/issues" }, "homepage": "https://github.com/jelmer/debian-lsp#readme", "engines": { "vscode": "^1.75.0" }, "categories": [ "Programming Languages" ], "keywords": [ "debian", "control", "copyright", "watch", "deb822", "upstream-metadata" ], "activationEvents": [ "onLanguage:debcontrol", "onLanguage:debcopyright", "onLanguage:debwatch", "onLanguage:debtestscontrol", "onLanguage:debchangelog", "onLanguage:debsourceformat", "onLanguage:debupstreammetadata" ], "main": "./out/extension.js", "contributes": { "languages": [ { "id": "debcontrol", "aliases": [ "Debian Control", "debcontrol" ], "filenamePatterns": [ "**/debian/control" ], "configuration": "./language-configuration.json" }, { "id": "debcopyright", "aliases": [ "Debian Copyright", "debcopyright" ], "filenamePatterns": [ "**/debian/copyright" ], "configuration": "./language-configuration.json" }, { "id": "debwatch", "aliases": [ "Debian Watch", "debwatch" ], "filenamePatterns": [ "**/debian/watch" ], "configuration": "./language-configuration.json" }, { "id": "debtestscontrol", "aliases": [ "Debian Tests Control", "debtestscontrol" ], "filenamePatterns": [ "**/debian/tests/control" ], "configuration": "./language-configuration.json" }, { "id": "debchangelog", "aliases": [ "Debian Changelog", "debchangelog" ], "filenamePatterns": [ "**/debian/changelog" ], "configuration": "./language-configuration.json" }, { "id": "debsourceformat", "aliases": [ "Debian Source Format", "debsourceformat" ], "filenamePatterns": [ "**/debian/source/format" ], "configuration": "./language-configuration.json" }, { "id": "debupstreammetadata", "aliases": [ "Debian Upstream Metadata", "debupstreammetadata" ], "filenamePatterns": [ "**/debian/upstream/metadata" ], "configuration": "./language-configuration.json" } ], "configuration": { "type": "object", "title": "Debian Language Server", "properties": { "debian.enable": { "type": "boolean", "default": true, "description": "Enable Debian language server" }, "debian.serverPath": { "type": "string", "default": "debian-lsp", "description": "Path to debian-lsp executable. If not in PATH, provide full path." }, "debian.trace.server": { "type": "string", "enum": [ "off", "messages", "verbose" ], "default": "off", "description": "Traces the communication between VS Code and the language server." } } }, "semanticTokenTypes": [ { "id": "debianField", "superType": "property", "description": "Known Debian control field" }, { "id": "debianUnknownField", "superType": "variable", "description": "Unknown or custom Debian control field" }, { "id": "debianValue", "superType": "string", "description": "Field value" }, { "id": "debianComment", "superType": "comment", "description": "Comment line" }, { "id": "changelogPackage", "superType": "namespace", "description": "Package name in changelog entry header" }, { "id": "changelogVersion", "superType": "number", "description": "Version string in changelog entry header" }, { "id": "changelogDistribution", "superType": "enumMember", "description": "Distribution name (e.g. unstable, UNRELEASED)" }, { "id": "changelogUrgency", "superType": "keyword", "description": "Urgency key in changelog metadata" }, { "id": "changelogMaintainer", "superType": "variable", "description": "Maintainer name and email in changelog trailer" }, { "id": "changelogTimestamp", "superType": "string", "description": "Timestamp in changelog trailer" }, { "id": "changelogMetadataValue", "superType": "string", "description": "Metadata value in changelog header (e.g. urgency level)" } ], "semanticTokenScopes": [ { "language": "debcontrol", "scopes": { "debianField": ["entity.name.tag"], "debianUnknownField": ["variable.other"], "debianValue": ["string"], "debianComment": ["comment"] } }, { "language": "debcopyright", "scopes": { "debianField": ["entity.name.tag"], "debianUnknownField": ["variable.other"], "debianValue": ["string"], "debianComment": ["comment"] } }, { "language": "debtestscontrol", "scopes": { "debianField": ["entity.name.tag"], "debianUnknownField": ["variable.other"], "debianValue": ["string"], "debianComment": ["comment"] } }, { "language": "debwatch", "scopes": { "debianField": ["entity.name.tag"], "debianUnknownField": ["variable.other"], "debianValue": ["string"], "debianComment": ["comment"] } }, { "language": "debupstreammetadata", "scopes": { "debianField": ["entity.name.tag"], "debianUnknownField": ["variable.other"], "debianValue": ["string"], "debianComment": ["comment"] } }, { "language": "debchangelog", "scopes": { "debianComment": ["comment"], "debianValue": ["string"], "changelogPackage": ["entity.name.namespace"], "changelogVersion": ["constant.numeric"], "changelogDistribution": ["support.constant"], "changelogUrgency": ["keyword.other"], "changelogMaintainer": ["variable.other"], "changelogTimestamp": ["string"], "changelogMetadataValue": ["string"] } } ] }, "scripts": { "compile": "tsc -p ./", "watch": "tsc -watch -p ./", "lint": "eslint src --ext ts", "package": "vsce package" }, "dependencies": { "vscode-languageclient": "^9.0.1" }, "devDependencies": { "@types/node": "^20.x", "@types/vscode": "^1.75.0", "@typescript-eslint/eslint-plugin": "^6.x", "@typescript-eslint/parser": "^6.x", "@vscode/vsce": "^2.x", "eslint": "^8.x", "typescript": "^5.x" } } debian-lsp-0.1.4/vscode-debian/publishing.md000064400000000000000000000014201046102023000170320ustar 00000000000000# Publishing ## Automated Publishing Create a git tag and push it: ```bash git tag v0.1.4 git push origin v0.1.4 ``` The GitHub Actions workflow will build and publish the extension. ## Registry Setup Add these as GitHub repository secrets: - `OPENVSX_TOKEN` - Get from https://open-vsx.org/user-settings/tokens (sign in with GitHub) - `VSCE_TOKEN` - Get from Azure DevOps (optional, for VS Code Marketplace) The workflow publishes to whichever registries have tokens configured. ## Manual Publishing ```bash npm run package npx ovsx publish -p YOUR_TOKEN # Open VSX npx vsce publish -p YOUR_TOKEN # VS Code Marketplace ``` ## Manual Installation Users can install the `.vsix` from GitHub Releases: ```bash code --install-extension vscode-debian-0.1.4.vsix ``` debian-lsp-0.1.4/vscode-debian/src/extension.ts000064400000000000000000000032621046102023000175250ustar 00000000000000import { workspace, ExtensionContext } from 'vscode'; import { LanguageClient, LanguageClientOptions, ServerOptions, TransportKind } from 'vscode-languageclient/node'; let client: LanguageClient; export function activate(context: ExtensionContext) { const config = workspace.getConfiguration('debian'); const isEnable = config.get('enable', true); if (!isEnable) { return; } const serverPath = config.get('serverPath', 'debian-lsp'); // Server options: spawn the debian-lsp executable const serverOptions: ServerOptions = { command: serverPath, args: [], transport: TransportKind.stdio }; // Client options: define which files the server should watch const clientOptions: LanguageClientOptions = { documentSelector: [ { scheme: 'file', language: 'debcontrol' }, { scheme: 'file', language: 'debcopyright' }, { scheme: 'file', language: 'debwatch' }, { scheme: 'file', language: 'debtestscontrol' }, { scheme: 'file', language: 'debchangelog' }, { scheme: 'file', language: 'debsourceformat' }, { scheme: 'file', language: 'debupstreammetadata' }, ], synchronize: { fileEvents: workspace.createFileSystemWatcher('**/debian/{control,copyright,watch,changelog,tests/control,source/format,upstream/metadata}') } }; // Create the language client and start it client = new LanguageClient( 'debian', 'Debian Language Server', serverOptions, clientOptions ); // Start the client (this will also launch the server) client.start(); } export function deactivate(): Thenable | undefined { if (!client) { return undefined; } return client.stop(); } debian-lsp-0.1.4/vscode-debian/tsconfig.json000064400000000000000000000006071046102023000170610ustar 00000000000000{ "compilerOptions": { "module": "commonjs", "target": "ES2020", "outDir": "out", "lib": ["ES2020"], "sourceMap": true, "rootDir": "src", "strict": true, "esModuleInterop": true, "skipLibCheck": true, "forceConsistentCasingInFileNames": true, "moduleResolution": "node" }, "include": ["src"], "exclude": ["node_modules", ".vscode-test"] }