pax_global_header00006660000000000000000000000064152244662600014521gustar00rootroot0000000000000052 comment=91d5f4e155faff03250cb4e9aa619affa5bc04b7 ldapts-ldapts-91d5f4e/000077500000000000000000000000001522446626000147205ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.claude/000077500000000000000000000000001522446626000162335ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.claude/skills/000077500000000000000000000000001522446626000175345ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.claude/skills/enforcing-typescript-standards/000077500000000000000000000000001522446626000256735ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.claude/skills/enforcing-typescript-standards/SKILL.md000066400000000000000000000311411522446626000270730ustar00rootroot00000000000000--- name: enforcing-typescript-standards description: Enforces the project's core TypeScript standards including explicit typing, import organization, class member ordering, and code safety rules. ALWAYS apply when creating, modifying, or reviewing any TypeScript (.ts/.tsx) file. --- # Enforcing TypeScript Standards Enforces the project's core TypeScript standards including explicit typing, import organization, class member ordering, and code safety rules. ## Triggers Activate this skill when the user says or implies any of these: - "write", "create", "implement", "add", "build" (new TypeScript code) - "fix", "update", "change", "modify", "refactor" (existing TypeScript code) - "review", "check", "improve", "clean up" (code quality) - Any request involving `.ts` or `.tsx` files Specific triggers: - Creating a new `.ts` or `.tsx` file - Modifying existing TypeScript code - Reviewing TypeScript code for compliance ## Core Standards ### Type Safety - **Explicit return types**: Prefer explicit return types when practical; omit when inference is obvious and adds no clarity - **Explicit member accessibility**: Class members require `public`, `private`, or `protected` - **Type-only imports**: Use `import type` for types: `import type { Foo } from './foo.js'` - **Sorted type constituents**: Union/intersection types must be alphabetically sorted - **Only throw Error objects**: Never throw strings or other primitives - **Avoid `any` and type assertions**: Prefer proper typing over `any` or `as` casts; use them only when truly necessary - **Type JSON fields explicitly**: Use `Record` or specific interfaces for JSON data, never `any` - **Use Number() for conversion**: Prefer `Number(value)` over `parseInt(value, 10)` or `parseFloat(value)` - **Reuse existing types**: Before defining a new interface, search for existing types that can be reused directly, extended, or derived using `Pick`, `Omit`, `Partial`, or other utility types #### Alternatives to Type Assertions Before using `as`, try these approaches in order: 1. Proper typing at the source 2. Type guards (`typeof`, `instanceof`) 3. Type narrowing through control flow 4. Custom type predicate functions 5. Discriminated unions ```ts // Bad const user = data as User; // Good function isUser(data: unknown): data is User { return typeof data === 'object' && data !== null && 'id' in data; } if (isUser(data)) { // data is now typed as User } ``` ### Import Organization - **Import order**: built-in → external → internal → parent → sibling → index (alphabetized within groups) - **No duplicate imports**: Consolidate imports from the same module - **Newline after imports**: Empty line required after import block ### Class Member Ordering 1. Signatures (call/construct) 2. Fields: private → public → protected 3. Constructors: public → protected → private 4. Methods: public → protected → private ### Code Style - **Simplicity over cleverness**: Straightforward, readable code is better than clever one-liners - **Early returns**: Use guard clauses to reduce nesting; return early for edge cases - **Nullish coalescing**: Prefer `??` over `||` for defaults (avoids false positives on `0` or `''`) - **Optional chaining**: Use `?.` for safe property access - **Match existing patterns**: Follow conventions already established in the codebase - **Meaningful identifiers**: Names must be descriptive (exceptions: `_`, `i`, `j`, `k`, `e`, `x`, `y`) - **Function declarations**: Use `function foo()` not `const foo = function()` - **Prefer const**: Use `const` unless reassignment is needed - **No var**: Always use `const` or `let` - **Object shorthand**: Use `{ foo }` not `{ foo: foo }` - **Template literals**: Use `` `Hello ${name}` `` not `'Hello ' + name` - **Strict equality**: Use `===` except for null comparisons - **One class per file**: Maximum one class definition per file - **Avoid `reduce`**: Prefer `for...of` loops or other array methods for clarity - **Functions over classes**: Prefer exported functions over classes with static methods (unless state is needed) - **No nested functions**: Define helper functions at module level, not inside other functions - **Immutability**: Create new objects/arrays instead of mutating existing ones ### Naming Conventions - **Enum members**: Use `PascalCase` (e.g., `MyValue`) - **No trailing underscores**: Identifiers cannot end with `_` ### Comments - **No redundant comments**: Never comment what the code already expresses clearly - **No duplicate comments**: Don't repeat information from function names, types, or nearby comments - **Meaningful only**: Only add comments to explain _why_, not _what_ — the code shows what it does ### Boolean Expressions - **Prefer truthiness checks**: Use implicit truthy/falsy checks over explicit comparisons - **Exception**: Use explicit checks when distinguishing `0`/`''` (valid values) from `null`/`undefined` is semantically important ### Testing - **Minimize mocking**: Avoid mocking everything; use real implementations and data generators when available - **Test real behavior**: Testing mocks provides little value — test actual code paths - **Don't be lazy**: Write thorough tests that cover edge cases, not just happy paths ### Error Handling - **Specific error types**: Prefer specific error types over generic `Error` when meaningful - **Avoid silent failures**: Don't swallow errors with empty catch blocks - **Handle rejections**: Always handle promise rejections - **Let errors propagate**: Don't catch errors just to re-throw or log — let them bubble up to error handlers ## Negative Knowledge Avoid these antipatterns: - `console.log()` statements in production code - `eval()` or `Function()` constructor - Nested ternary operators - `await` inside loops when `Promise.all` would be simpler (sequential awaits are fine when order matters or parallelism adds complexity) - Empty interfaces - Variable shadowing - Functions defined inside loops - `@ts-ignore` without explanation (use `@ts-expect-error` with 10+ char description) - Comments that restate the code: `// increment counter` above `counter++` - Comments that duplicate type information: `// returns a string` when return type is `: string` - Commented-out code (delete it; use version control) - Verbose boolean comparisons: `arr.length > 0`, `str !== ''`, `obj !== null && obj !== undefined` - Disabling linter rules via comments (fix the code instead) - Overuse of `any` type or `as` type assertions - Over-mocking in tests instead of using real implementations or data generators - Empty catch blocks that silently swallow errors - Using `||` for defaults when `??` is more appropriate - Deep nesting when early returns would simplify - Catching errors just to re-throw or log them - Nested function definitions inside other functions - Mutating objects/arrays instead of creating new ones - TOCTOU: Checking file/resource existence before operating (try and handle errors instead) - Classes with only static methods (use plain functions instead) - Duplicating existing interfaces instead of reusing or deriving with `Pick`/`Omit`/`Partial` ## Verification Workflow 1. **Analyze**: Compare the code change against these TypeScript standards 2. **Generate/Refactor**: Write or modify code to comply with all rules above 3. **Simplify**: Review for opportunities to simplify — prefer clear, straightforward code over clever solutions 4. **Review naming**: Verify variable and function names still make sense in context after changes 5. **Build**: Verify types compile without errors (e.g., `npm run build` or `npx tsc --noEmit`) 6. **Lint**: Run `npm run lint` to confirm compliance before completing the task ## Examples ### Comments Examples ```ts // Standard // Retry with exponential backoff to handle transient network failures async function fetchWithRetry(url: string, attempts = 3): Promise { for (let i = 0; i < attempts; i++) { try { return await fetch(url); } catch { await sleep(2 ** i * 100); } } throw new Error(`Failed after ${attempts} attempts`); } // Non-Standard /** * Fetches data from a URL with retry logic * @param url - The URL to fetch from * @param attempts - Number of attempts (default 3) * @returns A Promise that resolves to a Response */ async function fetchWithRetry(url: string, attempts = 3): Promise { // Loop through attempts for (let i = 0; i < attempts; i++) { try { // Try to fetch the URL return await fetch(url); } catch { // Wait before retrying await sleep(2 ** i * 100); } } // Throw error if all attempts fail throw new Error(`Failed after ${attempts} attempts`); } ``` ### Boolean Expressions Examples ```ts // Standard if (myArray.length) { } if (myString) { } if (myObject) { } if (!value) { } // Non-Standard if (myArray.length !== 0) { } if (myArray.length > 0) { } if (myString !== '') { } if (myObject !== null && myObject !== undefined) { } if (value === null || value === undefined) { } ``` ### Early Return Examples ```ts // Standard function processUser(user: User | null): Result { if (!user) { return { error: 'No user provided' }; } if (!user.isActive) { return { error: 'User is inactive' }; } return { data: transform(user) }; } // Non-Standard function processUser(user: User | null): Result { if (user) { if (user.isActive) { return { data: transform(user) }; } else { return { error: 'User is inactive' }; } } else { return { error: 'No user provided' }; } } ``` ### Functions Over Classes Examples ```ts // Standard export function calculateTotal(items: Item[]): number { return items.reduce((sum, item) => sum + item.price, 0); } export function formatCurrency(amount: number): string { return `$${amount.toFixed(2)}`; } // Non-Standard export class Calculator { static calculateTotal(items: Item[]): number { return items.reduce((sum, item) => sum + item.price, 0); } static formatCurrency(amount: number): string { return `$${amount.toFixed(2)}`; } } ``` ### No Nested Functions Examples ```ts // Standard function transformItem(item: Item): TransformedItem { return { id: item.id, name: item.name.toUpperCase() }; } async function processItems(items: Item[]): Promise { return items.map(transformItem); } // Non-Standard async function processItems(items: Item[]): Promise { function transformItem(item: Item): TransformedItem { return { id: item.id, name: item.name.toUpperCase() }; } return items.map(transformItem); } ``` ### Immutability Examples ```ts // Standard function addItem(items: Item[], newItem: Item): Item[] { return [...items, newItem]; } function removeItem(items: Item[], id: string): Item[] { return items.filter((item) => item.id !== id); } function updateItem(items: Item[], id: string, updates: Partial): Item[] { return items.map((item) => (item.id === id ? { ...item, ...updates } : item)); } // Non-Standard function addItem(items: Item[], newItem: Item): Item[] { items.push(newItem); return items; } function removeItem(items: Item[], id: string): Item[] { const index = items.findIndex((item) => item.id === id); items.splice(index, 1); return items; } ``` ### Error Propagation Examples ```ts // Standard async function getUser(id: string): Promise { return userService.findById(id); } // Non-Standard async function getUser(id: string): Promise { try { return await userService.findById(id); } catch (error) { console.error(error); throw error; } } ``` ### TOCTOU Examples ```ts // Standard async function readConfig(path: string): Promise { try { const content = await readFile(path, 'utf-8'); return JSON.parse(content); } catch (error) { if (isNotFoundError(error)) { return defaultConfig; } throw error; } } // Non-Standard async function readConfig(path: string): Promise { if (await fileExists(path)) { const content = await readFile(path, 'utf-8'); return JSON.parse(content); } return defaultConfig; } ``` ### Type Reuse Examples ```ts // Given an existing type interface User { id: string; email: string; name: string; passwordHash: string; createdAt: Date; updatedAt: Date; } // Standard - derive from existing type type PublicUser = Omit; type UserSummary = Pick; type UserUpdate = Partial>; // Non-Standard - duplicating fields that already exist interface PublicUser { id: string; email: string; name: string; createdAt: Date; updatedAt: Date; } interface UserSummary { id: string; name: string; } interface UserUpdate { email?: string; name?: string; } ``` ldapts-ldapts-91d5f4e/.devcontainer/000077500000000000000000000000001522446626000174575ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.devcontainer/devcontainer.json000066400000000000000000000004411522446626000230320ustar00rootroot00000000000000{ "name": "ldapts", "features": { "ghcr.io/devcontainers/features/node:2": { "version": "lts", "pnpmVersion": "latest" } }, "customizations": { "vscode": { "extensions": ["EditorConfig.EditorConfig"] } }, "postCreateCommand": "pnpm install" } ldapts-ldapts-91d5f4e/.editorconfig000077500000000000000000000002461522446626000174020ustar00rootroot00000000000000# editorconfig.org root = true [*] indent_style = space indent_size = 2 end_of_line = lf charset = utf-8 trim_trailing_whitespace = true insert_final_newline = true ldapts-ldapts-91d5f4e/.github/000077500000000000000000000000001522446626000162605ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.github/FUNDING.yml000066400000000000000000000011431522446626000200740ustar00rootroot00000000000000# These are supported funding model platforms github: [jgeurts] patreon: #jgeurts open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username otechie: # Replace with a single Otechie username custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ldapts-ldapts-91d5f4e/.github/linters/000077500000000000000000000000001522446626000177405ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.github/linters/.lintr000066400000000000000000000000601522446626000210650ustar00rootroot00000000000000linters: with_defaults(line_length_linter(240)) ldapts-ldapts-91d5f4e/.github/linters/.markdown-lint.yml000066400000000000000000000000321522446626000233220ustar00rootroot00000000000000MD013: line_length: 200 ldapts-ldapts-91d5f4e/.github/linters/.textlintrc.yml000066400000000000000000000004201522446626000227350ustar00rootroot00000000000000filters: allowlist: allow: - "/CHANGELOG\\.md$/" rules: filters: - comments rules: terminology: exclude: - URL - website - markdown - Bug Fixes - eslint - typescript - DNs - id ldapts-ldapts-91d5f4e/.github/renovate.json5000066400000000000000000000016131522446626000210640ustar00rootroot00000000000000{ $schema: 'https://docs.renovatebot.com/renovate-schema.json', extends: ['config:best-practices', ':semanticCommits'], timezone: 'America/Chicago', labels: ['dependencies'], rangeStrategy: 'pin', automerge: true, automergeType: 'pr', osvVulnerabilityAlerts: true, postUpdateOptions: ['pnpmDedupe'], packageRules: [ { matchDepTypes: ['peerDependencies'], enabled: false, }, { matchDepTypes: ['engines'], rangeStrategy: 'replace', }, { matchPackageNames: ['@types/node', '@types/asn1'], rangeStrategy: 'replace', }, { matchDepTypes: ['devDependencies'], schedule: ['* 0-4,22-23 * * 1-5', '* * * * 0,6'], groupName: 'dev dependencies', semanticCommitType: 'chore', }, { matchDepTypes: ['dependencies'], groupName: 'all dependencies', semanticCommitType: 'fix', }, ], } ldapts-ldapts-91d5f4e/.github/workflows/000077500000000000000000000000001522446626000203155ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.github/workflows/ci.yml000066400000000000000000000035131522446626000214350ustar00rootroot00000000000000name: Continuous Integration on: push: branches: - 'main' - 'issue-**' pull_request: branches: ['main'] permissions: contents: read jobs: build_and_test: name: 'Build & Test' runs-on: ubuntu-latest strategy: matrix: node-version: ['22', '24', '26'] steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js ${{ matrix.node-version }} uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: ${{ matrix.node-version }} cache: 'pnpm' - run: pnpm install --frozen-lockfile - name: Generate certificates run: node tests/data/generate-certs.mjs - name: Start openldap container run: docker compose up -d --wait - run: pnpm test env: CI: true - run: pnpm run build - run: pnpm run test:imports - name: Stopping openldap container run: docker compose down - name: Show OpenLDAP logs if: failure() run: docker logs ldapts-openldap-1 lint: name: 'Lint' runs-on: ubuntu-latest steps: - uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Use Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: lts/* cache: 'pnpm' - run: pnpm install --frozen-lockfile - name: Run lint run: pnpm run lint ldapts-ldapts-91d5f4e/.github/workflows/codeql-analysis.yml000066400000000000000000000062631522446626000241370ustar00rootroot00000000000000name: 'CodeQL' on: push: branches: ['main'] schedule: - cron: '0 15 * * 4' permissions: contents: read jobs: analyze: name: Analyze # Runner size impacts CodeQL analysis time. To learn more, please see: # - https://gh.io/recommended-hardware-resources-for-running-codeql # - https://gh.io/supported-runners-and-hardware-resources # - https://gh.io/using-larger-runners # Consider using larger runners for possible analysis time improvements. runs-on: ${{ (matrix.language == 'swift' && 'macos-latest') || 'ubuntu-latest' }} timeout-minutes: ${{ (matrix.language == 'swift' && 120) || 360 }} permissions: actions: read contents: read security-events: write strategy: fail-fast: false matrix: language: ['javascript-typescript'] # CodeQL supports [ 'c-cpp', 'csharp', 'go', 'java-kotlin', 'javascript-typescript', 'python', 'ruby', 'swift' ] # Use only 'java-kotlin' to analyze code written in Java, Kotlin or both # Use only 'javascript-typescript' to analyze code written in JavaScript, TypeScript or both # Learn more about CodeQL language support at https://aka.ms/codeql-docs/language-support steps: - name: Checkout repository uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: persist-credentials: false # Initializes the CodeQL tools for scanning. - name: Initialize CodeQL uses: github/codeql-action/init@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 with: languages: ${{ matrix.language }} # If you wish to specify custom queries, you can do so here or in a config file. # By default, queries listed here will override any specified in a config file. # Prefix the list here with "+" to use these queries and those in the config file. # For more details on CodeQL's query packs, refer to: https://docs.github.com/en/code-security/code-scanning/automatically-scanning-your-code-for-vulnerabilities-and-errors/configuring-code-scanning#using-queries-in-ql-packs # queries: security-extended,security-and-quality # Autobuild attempts to build any compiled languages (C/C++, C#, Go, Java, or Swift). # If this step fails, then you should remove it and run the build manually (see below) - name: Autobuild uses: github/codeql-action/autobuild@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 # ℹ️ Command-line programs to run using the OS shell. # 📚 See https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#jobsjob_idstepsrun # If the Autobuild fails above, remove it and uncomment the following three lines. # modify them (or add more) to build your code if your project, please refer to the EXAMPLE below for guidance. # - run: | # echo "Run, Build Application using script" # ./location_of_script_within_repo/buildscript.sh - name: Perform CodeQL Analysis uses: github/codeql-action/analyze@99df26d4f13ea111d4ec1a7dddef6063f76b97e9 # v4.37.0 with: category: '/language:${{matrix.language}}' ldapts-ldapts-91d5f4e/.github/workflows/lint.yml000066400000000000000000000035241522446626000220120ustar00rootroot00000000000000name: Lint on: push: branches: - main pull_request: branches: ['main'] permissions: contents: read packages: read statuses: write jobs: super_lint: name: Super Duper Linter runs-on: ubuntu-latest steps: - name: Checkout code uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: # super-linter needs the full git history to get the # list of files that changed across commits fetch-depth: 0 persist-credentials: false - name: Super-linter uses: super-linter/super-linter/slim@4ce20838b8ab83717e78138c5b3a1407148e0918 # v8.7.0 env: # To report GitHub Actions status checks GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} FILTER_REGEX_EXCLUDE: .*pnpm-lock\.yaml FIX_CSS_PRETTIER: false VALIDATE_ALL_CODEBASE: false VALIDATE_ANSIBLE: false VALIDATE_BIOME_FORMAT: false VALIDATE_BIOME_LINT: false VALIDATE_CLOUDFORMATION: false VALIDATE_CSS: false VALIDATE_CSS_PRETTIER: false VALIDATE_GITHUB_ACTIONS_ZIZMOR: false VALIDATE_HTML: false VALIDATE_HTML_PRETTIER: false VALIDATE_JSCPD: false VALIDATE_JSON: false VALIDATE_JSONC: false VALIDATE_JSONC_PRETTIER: false VALIDATE_JSON_PRETTIER: false VALIDATE_MARKDOWN_PRETTIER: false VALIDATE_YAML_PRETTIER: false VALIDATE_JAVASCRIPT_ES: false VALIDATE_JAVASCRIPT_PRETTIER: false VALIDATE_JAVASCRIPT_STANDARD: false VALIDATE_SPELL_CODESPELL: false VALIDATE_TSX: false VALIDATE_TYPESCRIPT_ES: false VALIDATE_TYPESCRIPT_PRETTIER: false VALIDATE_TYPESCRIPT_STANDARD: false DEFAULT_BRANCH: main ldapts-ldapts-91d5f4e/.github/workflows/release.yml000066400000000000000000000035251522446626000224650ustar00rootroot00000000000000name: Release on: push: branches: - main workflow_dispatch: permissions: contents: read jobs: release: name: Release permissions: contents: write # To be able to publish a GitHub release issues: write # To be able to comment on released issues pull-requests: write # To be able to comment on released pull requests id-token: write # To enable use of OIDC for npm provenance runs-on: ubuntu-latest environment: production steps: - name: Generate GitHub App token id: app-token uses: actions/create-github-app-token@bcd2ba49218906704ab6c1aa796996da409d3eb1 # v3.2.0 with: app-id: ${{ secrets.RELEASE_APP_ID }} private-key: ${{ secrets.RELEASE_APP_PRIVATE_KEY }} - name: Checkout uses: actions/checkout@9c091bb21b7c1c1d1991bb908d89e4e9dddfe3e0 # v7.0.0 with: fetch-depth: 0 persist-credentials: false token: ${{ steps.app-token.outputs.token }} - name: Setup pnpm uses: pnpm/action-setup@0ebf47130e4866e96fce0953f49152a61190b271 # v6.0.9 - name: Setup Node.js uses: actions/setup-node@48b55a011bda9f5d6aeb4c2d9c7362e8dae4041e # v6.4.0 with: node-version: 'lts/*' registry-url: 'https://registry.npmjs.org' cache: 'pnpm' - name: Install dependencies run: pnpm install --frozen-lockfile - name: Lint run: pnpm run lint - name: Build run: pnpm run build - name: Generate certificates run: node tests/data/generate-certs.mjs - name: Start openldap container run: docker compose up -d --wait - name: Test run: pnpm test - name: Release run: pnpm exec semantic-release env: GITHUB_TOKEN: ${{ steps.app-token.outputs.token }} ldapts-ldapts-91d5f4e/.github/workflows/semantic-pr-titles.yml000066400000000000000000000010401522446626000245570ustar00rootroot00000000000000name: Semantic PR Titles on: pull_request_target: types: - opened - edited - synchronize permissions: pull-requests: read jobs: main: name: Validate PR title runs-on: ubuntu-latest steps: - uses: amannn/action-semantic-pull-request@48f256284bd46cdaab1048c3721360e808335d50 # v6.1.1 env: GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} with: types: | docs feat fix test chore requireScope: false ldapts-ldapts-91d5f4e/.github/workflows/stale.yml000066400000000000000000000027141522446626000221540ustar00rootroot00000000000000name: Mark stale issues and pull requests on: schedule: - cron: '42 22 * * *' permissions: contents: read jobs: stale: runs-on: ubuntu-latest permissions: issues: write pull-requests: write steps: - uses: actions/stale@1e223db275d687790206a7acac4d1a11bd6fe629 # v10.4.0 with: repo-token: ${{ secrets.GITHUB_TOKEN }} stale-issue-message: 'This issue has been automatically marked as stale due to inactivity. If you believe this issue is still relevant, please add a comment to keep it open. Otherwise, it will be closed in the next few days. Thank you for your contributions!' stale-pr-message: 'This pull request has been automatically marked as stale due to inactivity. If you are still working on this, please add a comment or push new commits to prevent it from being closed. If no activity is detected, it will be closed soon. Thank you for your efforts!' close-issue-message: 'This issue has been automatically closed due to prolonged inactivity. If you believe this was done in error or the issue is still relevant, feel free to reopen or create a new issue. Thank you!' close-pr-message: 'This pull request has been automatically closed due to inactivity. If you would like to continue working on this, please feel free to reopen it or create a new pull request. Thank you for your contributions!' exempt-all-assignees: true exempt-issue-labels: accepted ldapts-ldapts-91d5f4e/.gitignore000066400000000000000000000003121522446626000167040ustar00rootroot00000000000000node_modules dump.rdb dist lib-cov *.seed *.log *.out *.pid npm-debug.log *~ *# .DS_STORE .netbeans nbproject .idea .node_history .sass-cache .vscode typings .eslintcache coverage tests/data/certs/* ldapts-ldapts-91d5f4e/.husky/000077500000000000000000000000001522446626000161415ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/.husky/.gitignore000066400000000000000000000000021522446626000201210ustar00rootroot00000000000000_ ldapts-ldapts-91d5f4e/.husky/pre-commit000077500000000000000000000002521522446626000201420ustar00rootroot00000000000000PATH=$PATH:/usr/local/bin:/opt/homebrew/bin echo 'NOTE: If node or pnpm is not found, you may need to run brew link for your specific node version' pnpm run lint-staged ldapts-ldapts-91d5f4e/.markdownlintignore000066400000000000000000000000151522446626000206320ustar00rootroot00000000000000CHANGELOG.md ldapts-ldapts-91d5f4e/.npmignore000066400000000000000000000004151522446626000167170ustar00rootroot00000000000000.git .gitignore .editorconfig .npmrc .github .idea .netbeans .mocharc.cjs tests src tsconfig.json tsconfig.lint.json tslint.json node_modules npm-debug.log .node_history *.swo *.swp *.swn *.swm *.seed *.log *.out *.pid lib-cov .DS_STORE *# *\# .\#* *~ .tmp dump.rdb ldapts-ldapts-91d5f4e/.npmrc000066400000000000000000000000201522446626000160300ustar00rootroot00000000000000save-exact=true ldapts-ldapts-91d5f4e/.prettierignore000066400000000000000000000000171522446626000177610ustar00rootroot00000000000000pnpm-lock.yaml ldapts-ldapts-91d5f4e/.textlintignore000066400000000000000000000000151522446626000177740ustar00rootroot00000000000000CHANGELOG.md ldapts-ldapts-91d5f4e/CHANGELOG.md000066400000000000000000000505571522446626000165450ustar00rootroot00000000000000# [9.0.0](https://github.com/ldapts/ldapts/compare/v8.2.0...v9.0.0) (2026-07-11) - chore!: require Node.js 22 or newer and remove deprecated Filter#escape shim ([#439](https://github.com/ldapts/ldapts/issues/439)) ([bc1b4d4](https://github.com/ldapts/ldapts/commit/bc1b4d4b9052ed930f1d9cc6010e1f41cd6d160d)), closes [Filter#escape](https://github.com/Filter/issues/escape) [Filter#escape](https://github.com/Filter/issues/escape) [#269](https://github.com/ldapts/ldapts/issues/269) [#348](https://github.com/ldapts/ldapts/issues/348) [#141](https://github.com/ldapts/ldapts/issues/141) [Filter#escape](https://github.com/Filter/issues/escape) [#401](https://github.com/ldapts/ldapts/issues/401) [#437](https://github.com/ldapts/ldapts/issues/437) ### BREAKING CHANGES - Node.js >= 22 is now required. - The deprecated `Filter#escape` instance method has been removed. Use the static `Filter.escape()` instead. - docs: fix terminology in connection factory docs - fix: allow lint-staged to commit ignored yaml files like pnpm-lock.yaml oxfmt exits non-zero when every file passed on the command line is excluded by ignore rules, which made any commit that staged pnpm-lock.yaml fail the pre-commit hook. # [8.2.0](https://github.com/ldapts/ldapts/compare/v8.1.8...v8.2.0) (2026-07-11) ### Bug Fixes - make Filter.escape() work as intended and document it ([#141](https://github.com/ldapts/ldapts/issues/141)) ([#401](https://github.com/ldapts/ldapts/issues/401)) ([eb17d99](https://github.com/ldapts/ldapts/commit/eb17d9952c0b5212a043d2d05cd2592633a2e6f3)) - release with a GitHub App token so the changelog commit can push to main ([#440](https://github.com/ldapts/ldapts/issues/440)) ([379a1aa](https://github.com/ldapts/ldapts/commit/379a1aafde30672f05bfe4c1d53d6dd69a3b6b64)) ### Features - add escapeFilter tagged template literal ([#141](https://github.com/ldapts/ldapts/issues/141)) ([#436](https://github.com/ldapts/ldapts/issues/436)) ([8b36980](https://github.com/ldapts/ldapts/commit/8b36980816a65014c110038c3554821a91832f32)), closes [Filter#escape](https://github.com/Filter/issues/escape) [#269](https://github.com/ldapts/ldapts/issues/269) [#348](https://github.com/ldapts/ldapts/issues/348) - allow custom connection factories ([#269](https://github.com/ldapts/ldapts/issues/269)) ([#434](https://github.com/ldapts/ldapts/issues/434)) ([952aa6c](https://github.com/ldapts/ldapts/commit/952aa6cde969c98cd41aa727c082a1a86cbd263b)), closes [Filter#escape](https://github.com/Filter/issues/escape) - optionally rebind automatically after reconnecting ([#348](https://github.com/ldapts/ldapts/issues/348)) ([#435](https://github.com/ldapts/ldapts/issues/435)) ([a02278d](https://github.com/ldapts/ldapts/commit/a02278d85c6d86a86338e42d2040698606754207)), closes [Filter#escape](https://github.com/Filter/issues/escape) [#269](https://github.com/ldapts/ldapts/issues/269) ## [8.1.8](https://github.com/ldapts/ldapts/compare/v8.1.7...v8.1.8) (2026-05-19) ## [8.1.7](https://github.com/ldapts/ldapts/compare/v8.1.6...v8.1.7) (2026-02-27) ## [8.1.6](https://github.com/ldapts/ldapts/compare/v8.1.5...v8.1.6) (2026-02-03) ### Bug Fixes - replace whatwg-url with native URL class ([#345](https://github.com/ldapts/ldapts/issues/345)) ([f61da30](https://github.com/ldapts/ldapts/commit/f61da30b430ff1f8e21b223fde0f965ae0f8e8ae)), closes [#344](https://github.com/ldapts/ldapts/issues/344) ## [8.1.5](https://github.com/ldapts/ldapts/compare/v8.1.4...v8.1.5) (2026-02-02) ### Bug Fixes - **deps:** update dependency whatwg-url to v16 ([#341](https://github.com/ldapts/ldapts/issues/341)) ([fd05995](https://github.com/ldapts/ldapts/commit/fd059958f5ac63f2afafa8315d7a348fa3582721)) ## [8.1.4](https://github.com/ldapts/ldapts/compare/v8.1.3...v8.1.4) (2026-01-31) ### Bug Fixes - export BerReader, BerWriter, and InvalidAsn1Error from main entry point ([#339](https://github.com/ldapts/ldapts/issues/339)) ([16bec3c](https://github.com/ldapts/ldapts/commit/16bec3c6fdf322cbe86bb0e37cf6afc6fefa906a)), closes [#318](https://github.com/ldapts/ldapts/issues/318) ## [8.1.3](https://github.com/ldapts/ldapts/compare/v8.1.2...v8.1.3) (2026-01-07) ## [8.1.2](https://github.com/ldapts/ldapts/compare/v8.1.1...v8.1.2) (2025-12-31) ## [8.1.1](https://github.com/ldapts/ldapts/compare/v8.1.0...v8.1.1) (2025-12-31) ### Bug Fixes - replace external asn1 package with internal BER module ([#309](https://github.com/ldapts/ldapts/issues/309)) ([8d50fbc](https://github.com/ldapts/ldapts/commit/8d50fbcb44e02db736112c1285a2e421d055d949)), closes [#151](https://github.com/ldapts/ldapts/issues/151) [#151](https://github.com/ldapts/ldapts/issues/151) # [8.1.0](https://github.com/ldapts/ldapts/compare/v8.0.36...v8.1.0) (2025-12-31) ### Features - add RFC 4517 postal address encoding/decoding utilities ([#308](https://github.com/ldapts/ldapts/issues/308)) ([cd8d4f6](https://github.com/ldapts/ldapts/commit/cd8d4f6a7ead941ca500033a358a82f9988953e0)), closes [#267](https://github.com/ldapts/ldapts/issues/267) ## [8.0.36](https://github.com/ldapts/ldapts/compare/v8.0.35...v8.0.36) (2025-12-29) ### Bug Fixes - treat empty tlsOptions as no TLS configuration ([#304](https://github.com/ldapts/ldapts/issues/304)) ([d0a894b](https://github.com/ldapts/ldapts/commit/d0a894bc75560e096def4217d2a49629882df6c5)), closes [#262](https://github.com/ldapts/ldapts/issues/262) ## [8.0.35](https://github.com/ldapts/ldapts/compare/v8.0.34...v8.0.35) (2025-12-25) ## [8.0.34](https://github.com/ldapts/ldapts/compare/v8.0.33...v8.0.34) (2025-12-24) ## [8.0.33](https://github.com/ldapts/ldapts/compare/v8.0.32...v8.0.33) (2025-12-24) ## [8.0.32](https://github.com/ldapts/ldapts/compare/v8.0.31...v8.0.32) (2025-12-23) ## [8.0.31](https://github.com/ldapts/ldapts/compare/v8.0.30...v8.0.31) (2025-12-22) ## [8.0.30](https://github.com/ldapts/ldapts/compare/v8.0.29...v8.0.30) (2025-12-20) ## [8.0.29](https://github.com/ldapts/ldapts/compare/v8.0.28...v8.0.29) (2025-12-20) ## [8.0.28](https://github.com/ldapts/ldapts/compare/v8.0.27...v8.0.28) (2025-12-19) ## [8.0.27](https://github.com/ldapts/ldapts/compare/v8.0.26...v8.0.27) (2025-12-18) ## [8.0.26](https://github.com/ldapts/ldapts/compare/v8.0.25...v8.0.26) (2025-12-18) ## [8.0.25](https://github.com/ldapts/ldapts/compare/v8.0.24...v8.0.25) (2025-12-16) ## [8.0.24](https://github.com/ldapts/ldapts/compare/v8.0.23...v8.0.24) (2025-12-15) ## [8.0.23](https://github.com/ldapts/ldapts/compare/v8.0.22...v8.0.23) (2025-12-14) ## [8.0.22](https://github.com/ldapts/ldapts/compare/v8.0.21...v8.0.22) (2025-12-14) ## [8.0.21](https://github.com/ldapts/ldapts/compare/v8.0.20...v8.0.21) (2025-12-13) ## [8.0.20](https://github.com/ldapts/ldapts/compare/v8.0.19...v8.0.20) (2025-12-12) ## [8.0.19](https://github.com/ldapts/ldapts/compare/v8.0.18...v8.0.19) (2025-12-11) ## [8.0.18](https://github.com/ldapts/ldapts/compare/v8.0.17...v8.0.18) (2025-12-09) ## [8.0.17](https://github.com/ldapts/ldapts/compare/v8.0.16...v8.0.17) (2025-12-08) ## [8.0.16](https://github.com/ldapts/ldapts/compare/v8.0.15...v8.0.16) (2025-12-08) ## [8.0.15](https://github.com/ldapts/ldapts/compare/v8.0.14...v8.0.15) (2025-12-08) ## [8.0.14](https://github.com/ldapts/ldapts/compare/v8.0.13...v8.0.14) (2025-12-06) ## [8.0.13](https://github.com/ldapts/ldapts/compare/v8.0.12...v8.0.13) (2025-12-05) ### Bug Fixes - Remove uuid package to fix cjs imports ([6914379](https://github.com/ldapts/ldapts/commit/6914379c7ebe1c652f4b644846ede3da58f4e9c4)), closes [#277](https://github.com/ldapts/ldapts/issues/277) ## [8.0.12](https://github.com/ldapts/ldapts/compare/v8.0.11...v8.0.12) (2025-12-04) ## [8.0.11](https://github.com/ldapts/ldapts/compare/v8.0.10...v8.0.11) (2025-12-03) ## [8.0.10](https://github.com/ldapts/ldapts/compare/v8.0.9...v8.0.10) (2025-12-03) ### Bug Fixes - **deps:** update all dependencies ([#268](https://github.com/ldapts/ldapts/issues/268)) ([223b403](https://github.com/ldapts/ldapts/commit/223b403b47b36b3342da35e797acfeb13887a4ce)) ## [8.0.9](https://github.com/ldapts/ldapts/compare/v8.0.8...v8.0.9) (2025-07-28) ## [8.0.8](https://github.com/ldapts/ldapts/compare/v8.0.7...v8.0.8) (2025-07-21) ### Bug Fixes - avoid extra properties on Entry objects for case mis-match ([#247](https://github.com/ldapts/ldapts/issues/247)) ([d56e2f6](https://github.com/ldapts/ldapts/commit/d56e2f619a34c3fc8caaf623442fd212c55cb5dc)) ## [8.0.7](https://github.com/ldapts/ldapts/compare/v8.0.6...v8.0.7) (2025-07-21) ## [8.0.6](https://github.com/ldapts/ldapts/compare/v8.0.5...v8.0.6) (2025-07-14) ## [8.0.5](https://github.com/ldapts/ldapts/compare/v8.0.4...v8.0.5) (2025-07-07) ## [8.0.4](https://github.com/ldapts/ldapts/compare/v8.0.3...v8.0.4) (2025-06-30) ## [8.0.3](https://github.com/ldapts/ldapts/compare/v8.0.2...v8.0.3) (2025-06-30) ## [8.0.2](https://github.com/ldapts/ldapts/compare/v8.0.1...v8.0.2) (2025-06-26) ## [8.0.1](https://github.com/ldapts/ldapts/compare/v8.0.0...v8.0.1) (2025-05-29) ### Bug Fixes - **deps:** update all dependencies ([#211](https://github.com/ldapts/ldapts/issues/211)) ([02c585f](https://github.com/ldapts/ldapts/commit/02c585f63ff19c6a476197e7ad4e833be01ceec5)) # [8.0.0](https://github.com/ldapts/ldapts/compare/v7.4.0...v8.0.0) (2025-05-05) - feat!: remove Node.js v18 support ([#203](https://github.com/ldapts/ldapts/issues/203)) ([da031c0](https://github.com/ldapts/ldapts/commit/da031c078d7bd12ad7c348086fa214bc673a3fc2)) ### Bug Fixes - optional scope for semantic release ([#189](https://github.com/ldapts/ldapts/issues/189)) ([6e36018](https://github.com/ldapts/ldapts/commit/6e360182f5f42fa69120aa0829cecf665b9f8744)) ### BREAKING CHANGES - Drop support for Node.js v18. Minimum required version is now Node.js v20. * Updated engines field in package.json * Updated CI configuration to test on supported versions only - Run CI jobs for PRs targeting main # [7.4.0](https://github.com/ldapts/ldapts/compare/v7.3.3...v7.4.0) (2025-04-07) ### Features - ensure socket is destroyed after unbind and connection errors ([#180](https://github.com/ldapts/ldapts/issues/180)) ([bcf433c](https://github.com/ldapts/ldapts/commit/bcf433c884b192a0e1af6032dfe34dc09c3c8493)) # 7.3.3 - 2024-03-24 - feat: MoreResultsToReturn error, useful for informing end users by @ayZagen in - fix: message timers are not cleared by @ayZagen in - fix: ensure connectTimer is cleared by @ayZagen in - Replace JumpCloud with local openldap image. Thank you @ayZagen! - Update npms # 7.3.2 - 2024-03-10 - Update npms # 7.3.1 - 2024-01-08 - Include Filter type definition. Fix #164. Thank you @ddequidt! - Update npms # 7.3.0 - 2024-12-17 - Update npms - Use node protocol for built-in modules. #163 Than you @ayZagen! # 7.2.2 - 2024-11-29 - Fix modifyDN newSuperior not working with the long form. #162 Thank you @Oh-suki! - Update npms # 7.2.1 - 2024-09-30 - Fix Property 'asyncDispose' does not exist on type 'SymbolConstructor'. #158 Thank you @ayZagen! - Update npms # 7.2.0 - 2024-09-10 - Make Client disposable. #155 Thank you @ayZagen! - Allow search to be paginated via searchPaginated. #156 Thank you @ayZagen! - Allow subordinates to be used as search scope. #157 Thank you @ayZagen! - Update npms # 7.1.1 - 2024-08-26 - Update npms - Replace deprecated `parse` from url module with `parseURL` from whatwg-url - Replace deprecated `string#substr` and `buffer#slice` usage # 7.1.0 - 2024-07-09 - Ensure errors have name and prototype set - Update npms - Update eslint to use flat config # 7.0.12 - 2024-05-13 - Update npms # 7.0.11 - 2024-04-08 - Fix DN clone method when RDNs array is not empty. Fix #149 - Update npms # 7.0.10 - 2024-03-11 - Update npms # 7.0.9 - 2024-02-07 - Update npms # 7.0.8 - 2024-01-05 - Update npms # 7.0.7 - 2023-11-28 - Update npms # 7.0.6 - 2023-10-27 - Update npms # 7.0.5 - 2023-10-10 - Fix CommonJS package issues. NOTE: All exports are at the root level now. For example: `import { Control } from 'ldapts/controls';` is now `import { Control } from 'ldapts';` - Include `src` in npm package # 7.0.4 - 2023-10-09 - Fix toString output for OrFilter and NotFilter # 7.0.3 - 2023-10-06 - Fix asn1 import statements # 7.0.2 - 2023-10-06 - Update some missing import/export statements to include extensions - Enable `allowSyntheticDefaultImports` to fix asn1 import statements # 7.0.1 - 2023-10-05 - Update import/export statements to include extensions # 7.0.0 - 2023-10-05 - Drop Node.js 16 support - Updated to ES module - Changed mocha test runner to use tsx instead of ts-node - Update npms # 6.0.0 - 2023-07-24 - Update npms - Fix lots of linting issues - Change `Client.messageDetailsByMessageId` to be a map - Fix `Client._send` signature to return `undefined` - Change `MessageResponseStatus`, `ProtocolOperation`, and `SearchFilter` from enum to a const - Enforce `toString()` definition for filters # 5.0.0 - 2023-07-18 - Drop Node.js 14 support - Update npms - Add OpenLDAP test server! Fix #135 Thanks @tsaarni! - Allow for optional password by setting a default empty string. Fix #134 Thanks @wattry, @TimoHocker, and @thernstig! - Fix reading controls from responses. Fix #106 # 4.2.6 - 2023-04-28 - Update npms # 4.2.5 - 2023-04-17 - Update npms # 4.2.4 - 2023-02-21 - Check for socket if short-circuiting `_connect()` # 4.2.3 - 2023-02-20 - Update npms - Fix socket connection not established error. Fix #127 Thanks @Templum! # 4.2.2 - 2023-01-03 - Update npms - Enable noUncheckedIndexedAccess compiler option # 4.2.1 - 2022-10-11 - Update npms # 4.2.0 - 2022-09-14 - Add DIGEST-MD5 and SCRAM-SHA-1 SASL mechanisms (PR #120). Thanks @TimoHocker! - Update npms # 4.1.1 - 2022-08-30 - Update npms # 4.1.0 - 2022-06-24 - Remove automatically appending ;binary to attributes. Fix #114 - Update npms # 4.0.0 - 2022-05-23 - Drop Node.js 12 support - Update npms # 3.2.4 - 2022-04-13 - Update npms # 3.2.3 - 2022-03-22 - Update npms # 3.2.2 - 2022-02-22 - Update npms - Update husky to support Apple silicon Homebrew package links # 3.2.1 - 2021-12-30 - Update npms - Expand type definition version constraints. Fix #108 # 3.2.0 - 2021-12-21 - Fix SASL authentication. Thanks @wattry! - Update npms # 3.1.2 - 2021-11-16 - Update npms # 3.1.1 - 2021-10-29 - Update npms - Format markdown files # 3.1.0 - 2021-09-20 - Allow EqualityFilter to accept Buffer as a value # 3.0.7 - 2021-09-14 - Update npms # 3.0.6 - Update npms # 3.0.5 - Add documentation for `explicitBufferAttributes` - Format and lint markdown files # 3.0.4 - Fix relative path in source maps. Fixes #102. Thanks @stevenhair! # 3.0.3 - Update npms # 3.0.2 - Update npms # 3.0.1 - Fix "Unhandled promise rejection" when calling modify without password. Fix #88. Thanks @ctaschereau! - Enable TypeScript lint checks: [`noPropertyAccessFromIndexSignature`](https://www.typescriptlang.org/tsconfig#noPropertyAccessFromIndexSignature) and [`noImplicitOverride`](https://www.typescriptlang.org/tsconfig#noImplicitOverride) - Update npms # 3.0.0 - Drop Node.js 10 support - Add Node.js v16 to CI tests - Update npms - Allow `timeLimit: 0` in search options. Fix #97. Thanks @liudonghua123! # 2.12.0 - Export error classes. Fix #93 - Redact password field from debug logging during send(). Fix #94 - Update npms - Enable package-lock.json to speed up CI builds # 2.11.1 - Update npms # 2.11.0 - Update npms - Sort union/intersection members - Revert remove sequence identifier for SASL authentication # 2.10.1 - Update npms - Fix documentation for SASL authentication - Remove sequence identifier for SASL authentication # 2.10.0 - Add support for PLAIN and EXTERNAL SASL authentication to bind request # 2.9.1 - Simplify control import directives # 2.9.0 - Update npms - Improve Control usability and provide example test for search with a custom Control. Fix #91 # 2.8.1 - Fix null/undefined values for attributes when calling add(). Fix #88 # 2.8.0 - Fix modifyDN to ignore escaped commas when determining NewSuperior. PR #87 Thanks @hasegawa-jun! - Add tests for modifyDN - Update npms - Format code with prettier # 2.7.0 - Support NewSuperior with modifyDN. PR #84 Thanks @IsraelFrid! - Update npms # 2.6.1 - Added documentation for `explicitBufferAttributes` attribute # 2.6.0 - Update npms - Expose parsedBuffers on Attribute and added `explicitBufferAttributes` to search options. Fix #72 and Fix #82 # 2.5.1 - Update npms # 2.5.0 - Update @types/node npm to latest version. Fix #73 - Add mocharc file # 2.4.0 - Add Buffer as value type for client.exop(). Fixes #74 # 2.3.0 - Update npms - Update Typescript to v3.9 # 2.2.1 - Update npms # 2.2.0 - Support `startTLS` for upgrading an existing connection to be encrypted. Fix #71 - Fix type of `tlsOptions` to `tls.ConnectionOptions` in `Client` constructor options - Fix sending exop with empty/undefined value - Add `.id` to internal socket to allow cleanup when unbinding after startTLS # 2.1.0 - Use secure connection if `tlsOptions` is specified or if url starts with `ldaps:` when constructing a client. Fix #71 # 2.0.3 - Update npms - Make typescript linter rules more strict # 2.0.2 - Ignore case when determining if attribute is binary. Fix #11 # 2.0.1 - Documentation updates # 2.0.0 - Drop support for Node.js v8 - Update to Typescript 3.7 - Fix exop response overwriting status and error message. Fixes #52 - Update npms - Improve documentation. Lots of :heart: for ldapjs docs, [ldapwiki](https://ldapwiki.com/), and [ldap.com](https://ldap.com/ldapv3-wire-protocol-reference/) docs. Fix #31 # 1.10.0 - Include original error message with exceptions. Fix #36 - Include all requested attributes with search results. Fix #22 - Add isConnected to Client. Fix #25 - Try to fix socket ending and reference handling issues. Thanks @december1981! Fix #24 - Update npms # 1.9.0 - Export Change and Attribute classes. Thanks @willmcenaney! - Parse search filter before sending partial request. Thanks @markhatchell! # 1.8.0 - Remove "dist" folder from published npm - Include type definitions as "dependencies" instead of "devDependencies" - Update npms # 1.7.0 - Add DN class as alternate option for specifying DNs. Thanks @adrianplavka! - Update npms # 1.6.0 - Fix incorrectly escaping search filter names/values. Fix #18 # 1.5.1 - Do not throw "Size limit exceeded" error if `sizeLimit` is defined and the server responds with `4` (Size limit exceeded). Note: It seems that items are returned even though the return status is `4` (Size limit exceeded). I'm not really sure what to do in that case. At this time, I decided against throwing an error and instead just returning the results returned thus far. That approach works with JumpCloud and forumsys' ldap servers # 1.5.0 - Update dependencies - Only include PagedResultsControl if `searchOptions.paged` is specified. Fixes #17 - Make Filter.escape() public. Thanks @stiller-leser! - Fix FilterParser parsing of ExtensibleFilters to include attribute type. Hopefully fixes #16 # 1.4.2 - Update dependencies - Add documentation for search options # 1.4.1 - Fix 'Socket connection not established' when server closes the connection (Fix #13). Thanks @trevh3! # 1.4.0 - Support binary attribute values (Fix #11) # 1.3.0 - Add Entry interface for SearchEntry. Thanks @hikaru7719! # 1.2.3 - Move asn1 type definitions to DefinitelyTyped # 1.2.2 - Fix error message for InvalidCredentialsError # 1.2.1 - Provide exports for public classes: errors, filters, and messages (Fix #4) # 1.2.0 - Fix escaping filter attribute names and values # 1.1.4 - Fix Add and Modify to handle the response from the server. Thanks @adrianplavka! # 1.1.3 - Update dev dependencies # 1.1.2 - Fix ECONNRESET issue connecting to non-secure endpoint - Throw an error for each message on socket error # 1.1.1 - Add original string to error message when parsing filters - Adjust parsing & and | in filters - Add more filter parsing tests # 1.1.0 - Add client.add() and client.modify() # 1.0.6 - Use hex for message type code in closed message error message - Add additional test for calling unbind() multiple times # 1.0.5 - Add message name to error message when socket is closed before message response # 1.0.4 - Add type definitions for asn1 - Add message type id to error when cleaning pending messages. - Force protocolOperation to be defined for Message types # 1.0.3 - Verify the socket exists before sending unbind message # 1.0.2 - Setup prepublish to always build. - Push fix from 1.0.1 # 1.0.1 - Fix search to return attribute values by default # 1.0.0 - Initial release ldapts-ldapts-91d5f4e/CODE_OF_CONDUCT.md000066400000000000000000000064601522446626000175250ustar00rootroot00000000000000# Contributor Covenant Code of Conduct ## Our Pledge In the interest of fostering an open and welcoming environment, we as contributors and maintainers pledge to make participation in our project and our community a harassment-free experience for everyone, regardless of age, body size, disability, ethnicity, sex characteristics, gender identity and expression, level of experience, education, socio-economic status, nationality, personal appearance, race, religion, or sexual identity and orientation. ## Our Standards Examples of behavior that contributes to creating a positive environment include: - Using welcoming and inclusive language - Being respectful of differing viewpoints and experiences - Gracefully accepting constructive criticism - Focusing on what is best for the community - Showing empathy towards other community members Examples of unacceptable behavior by participants include: - The use of sexualized language or imagery and unwelcome sexual attention or advances - Trolling, insulting/derogatory comments, and personal or political attacks - Public or private harassment - Publishing others' private information, such as a physical or electronic address, without explicit permission - Other conduct which could reasonably be considered inappropriate in a professional setting ## Our Responsibilities Project maintainers are responsible for clarifying the standards of acceptable behavior and are expected to take appropriate and fair corrective action in response to any instances of unacceptable behavior. Project maintainers have the right and responsibility to remove, edit, or reject comments, commits, code, wiki edits, issues, and other contributions that are not aligned to this Code of Conduct, or to ban temporarily or permanently any contributor for other behaviors that they deem inappropriate, threatening, offensive, or harmful. ## Scope This Code of Conduct applies within all project spaces, and it also applies when an individual is representing the project or its community in public spaces. Examples of representing a project or community include using an official project e-mail address, posting via an official social media account, or acting as an appointed representative at an online or offline event. Representation of a project may be further defined and clarified by project maintainers. ## Enforcement Instances of abusive, harassing, or otherwise unacceptable behavior may be reported by contacting the project team at . All complaints will be reviewed and investigated and will result in a response that is deemed necessary and appropriate to the circumstances. The project team is obligated to maintain confidentiality with regard to the reporter of an incident. Further details of specific enforcement policies may be posted separately. Project maintainers who do not follow or enforce the Code of Conduct in good faith may face temporary or permanent repercussions as determined by other members of the project's leadership. ## Attribution This Code of Conduct is adapted from the [Contributor Covenant][homepage], version 1.4, available at [homepage]: https://www.contributor-covenant.org For answers to common questions about this code of conduct, see ldapts-ldapts-91d5f4e/CONTRIBUTING.md000066400000000000000000000013221522446626000171470ustar00rootroot00000000000000# Contributing Guidelines ## Pull Request Format The title of your PR should match the following format: ```text : ``` ### Types - **docs** - Documentation changes only - **feat** - Any new functionality additions - **fix** - Bugfixes that don't add new functionality - **test** - Test changes only - **chore** - Anything else Within the body of your PR, make sure you reference the issue that you have worked on, as well as pointing out anything of note you wish us to look at during our review. ### Commits We do not care about the number, or style of commits in your history, because we squash merge every PR into main. Feel free to commit in whatever style you feel comfortable with. ldapts-ldapts-91d5f4e/LICENSE000066400000000000000000000020501522446626000157220ustar00rootroot00000000000000Copyright (c) 2018 jim@biacreations.com Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ldapts-ldapts-91d5f4e/README.md000066400000000000000000001152231522446626000162030ustar00rootroot00000000000000# LDAPts [![NPM version](https://img.shields.io/npm/v/ldapts.svg?style=flat)](https://npmjs.org/package/ldapts) [![node version](https://img.shields.io/node/v/ldapts.svg?style=flat)](https://nodejs.org) [![Known Vulnerabilities](https://snyk.io/test/npm/ldapts/badge.svg)](https://snyk.io/test/npm/ldapts) Providing an API to access LDAP directory servers from Node.js programs. ## Table of Contents - [API Details](#api-details) - [Create a client](#create-a-client) - [Specifying Controls](#specifying-controls) - [bind](#bind) - [startTLS](#starttls) - [add](#add) - [compare](#compare) - [del](#del) - [exop](#exop) - [modify](#modify) - [Change](#change) - [modifyDN](#modifydn) - [search](#search) - [Filter Strings](#filter-strings) - [Return buffer for specific attribute](#return-buffer-for-specific-attribute) - [searchPaginated](#searchpaginated) - [unbind](#unbind) - [Usage Examples](#usage-examples) - [Authenticate example](#authenticate-example) - [Search example](#search-example) - [Delete Active Directory entry example](#delete-active-directory-entry-example) - [Configuring Secure Connections](#configuring-secure-connections) - [Common Errors](#common-errors) - [Development](#development) - [Start test OpenLDAP server](#start-test-openldap-server) - [Close test OpenLDAP server](#close-test-openldap-server) ## API details ### Create a client The code to create a new client looks like: ```ts import { Client } from 'ldapts'; const client = new Client({ url: 'ldaps://ldap.jumpcloud.com', timeout: 0, connectTimeout: 0, tlsOptions: { minVersion: 'TLSv1.2', }, strictDN: true, }); ``` You can use `ldap://` or `ldaps://`; the latter would connect over SSL (note that this will not use the LDAP TLS extended operation, but literally an SSL connection to port 636, as in LDAP v2). The full set of options to create a client is: | Attribute | Description | | ------------------------ | ----------------------------------------------------------------------------------------------------------- | | `url` | A valid LDAP URL (proto/host/port only) | | `timeout` | Milliseconds client should let operations live for before timing out (Default: Infinity) | | `connectTimeout` | Milliseconds client should wait before timing out on TCP connections (Default: OS default) | | `tlsOptions` | TLS [connect() options](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | | `strictDN` | Force strict DN parsing for client methods (Default is true) | | `createConnection` | Custom connection factory for `ldap://` URLs. Called with the parsed port and host (Default: `net.connect`) | | `createSecureConnection` | Custom connection factory for `ldaps://` URLs and `startTLS()` upgrades (Default: `tls.connect`) | | `autoRebind` | Automatically replay the last successful bind after the connection is re-established (Default: false) | #### Custom connection factories If you need control over how the underlying socket is created — a proxied or tunneled connection, a Unix socket, a pre-established socket, or a non-Node transport — pass a `createConnection` (for `ldap://`) and/or `createSecureConnection` (for `ldaps://` and `startTLS()`) function. When omitted, the client uses `net.connect`/`tls.connect` as before. ```ts import * as net from 'node:net'; import { Client } from 'ldapts'; const socket = net.connect(389, '192.168.2.163'); const client = new Client({ url: 'ldap://192.168.2.163:389', createConnection: () => socket, }); ``` Note that the client transparently reconnects when it is used after being unbound or after the server closes the connection, so the factory may be invoked more than once and should generally create a fresh connection per call. #### Automatic rebind The client transparently reconnects when an operation is issued after the server closed the connection (idle timeout, restart, network hiccup). By default that new connection is **unauthenticated**, which historically required this workaround before every operation: ```ts if (!client.isConnected) { await client.bind(bindDN, password); } ``` With `autoRebind: true`, the client remembers the last successful `bind()` and replays it automatically after reconnecting: ```ts const client = new Client({ url: 'ldap://localhost:389', autoRebind: true, }); await client.bind(bindDN, password); // ...hours later, after the server dropped the connection... const result = await client.search(searchDN); // reconnects and rebinds automatically ``` You can also check the current authentication state with `client.isBound`, which becomes `false` whenever the connection is closed or re-established. Things to be aware of: - The bind credentials are kept in memory for the lifetime of the client. `unbind()` clears them. - Sessions upgraded with `startTLS()` are not automatically rebound: the replayed bind would be sent before the fresh connection could be upgraded again, so credentials bound after a `startTLS()` upgrade are never cached. ### Specifying Controls Single or an array of `Control` objects can be added to various operations like the following: ```ts import { Control } from 'ldapts/controls'; const { searchEntries, searchReferences } = await client.search( searchDN, { filter: '(mail=peter.parker@marvel.com)', }, new Control('1.2.840.113556.1.4.417'), ); ``` You can also subclass `Control` for finer control over how data is parsed and written. Look at [PagedResultsControl](src/controls/PagedResultsControl.ts) for an example. ### bind `bind(dnOrSaslMechanism, [password], [controls])` Performs a bind operation against the LDAP server. Arguments: | Argument | Description | | ------------------------------------- | --------------------------------------------------------------------------------------------------------------- | | `dnOrSaslMechanism` (string) | The name (DN) of the directory object that the client wishes to bind as or the SASL mechanism (PLAIN, EXTERNAL) | | `[password]` (string) | Password for the target bind DN. For SASL this is instead an optional set of encoded SASL credentials. | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Simple Example: ```ts await client.bind('cn=root', 'secret'); ``` SASL Example: ```ts // No credentials await client.bind('EXTERNAL'); // With credentials const credentials = '...foo...'; await client.bind('PLAIN', credentials); ``` ### startTLS `startTLS(options, [controls])` Performs a StartTLS extended operation against the LDAP server to initiate a TLS-secured communication channel over an otherwise clear-text connection. Arguments: | Argument | Description | | ------------------------------------- | ----------------------------------------------------------------------------------------- | | `options` (object) | TLS [connect() options](https://nodejs.org/api/tls.html#tls_tls_connect_options_callback) | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example: ```ts await client.startTLS({ ca: [fs.readFileSync('mycacert.pem')], }); ``` ### add `add(dn, entry, [controls])` Performs an add operation against the LDAP server. Allows you to add an entry (as a js object or array of Attributes), and as always, controls are optional. Arguments: | Argument | Description | | ------------------------------------- | ------------------------------------------------------- | | `dn` (string) | The DN of the entry to add | | `entry` (object|Attribute[]) | The set of attributes to include in that entry | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example: ```ts var entry = { cn: 'foo', sn: 'bar', email: ['foo@bar.com', 'foo1@bar.com'], objectclass: 'fooPerson', }; await client.add('cn=foo, o=example', entry); ``` ### compare `compare(dn, attribute, value, [controls])` Performs an LDAP compare operation with the given attribute and value against the entry referenced by dn. Arguments: | Argument | Description | | ------------------------------------- | ----------------------------------------------------------------------- | | `dn` (string) | The DN of the entry in which the comparison is to be made | | `attribute` (string) | The Name of the attribute in which the comparison is to be made | | `value` (string) | The Attribute Value Assertion to try to find in the specified attribute | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Returns: `(boolean)`: Returns `true` if the target entry exists and does contain the specified attribute value; otherwise `false` Example: ```ts const hasValue = await client.compare('cn=foo, o=example', 'sn', 'bar'); ``` ### del `del(dn, [controls])` Deletes an entry from the LDAP server. Arguments: | Argument | Description | | ------------------------------------- | ------------------------------------------------------- | | `dn` (string) | The DN of the entry to delete | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example: ```ts await client.del('cn=foo, o=example'); ``` ### exop `exop(oid, [value], [controls])` Performs an LDAP extended operation against an LDAP server. Arguments: | Argument | Description | | ------------------------------------- | ------------------------------------------------------- | | `oid` (string) | Object identifier representing the type of request | | `[value]` (string) | Optional value - based on the type of operation | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example (performs an LDAP 'whoami' extended op): ```ts const { value } = await client.exop('1.3.6.1.4.1.4203.1.11.3'); ``` ### modify `modify(name, changes, [controls])` Performs an LDAP modify operation against the LDAP server. This API requires you to pass in a `Change` object, which is described below. Note that you can pass in a single `Change` or an array of `Change` objects. Arguments: | Argument | Description | | ------------------------------------- | ------------------------------------------------------- | | `dn` (string) | The DN of the entry to modify | | `changes` (Change|Change[]) | The set of changes to make to the entry | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example (update multiple attributes): ```ts import { Attribute, Change } from 'ldapts'; await client.modify('cn=foo, o=example', [ new Change({ operation: 'replace', modification: new Attribute({ type: 'title', values: ['web tester'] }) }), new Change({ operation: 'replace', modification: new Attribute({ type: 'displayName', values: ['John W Doe'] }) }), ]); ``` Example (update binary attribute): ```ts import { Attribute, Change } from 'ldapts'; const thumbnailPhotoBuffer = await fs.readFile(path.join(__dirname, './groot_100.jpg')); var change = new Change({ operation: 'replace', modification: new Attribute({ type: 'thumbnailPhoto;binary', values: [thumbnailPhotoBuffer], }), }); await client.modify('cn=foo, o=example', change); ``` #### Change `Change({ operation, modification })` A `Change` object maps to the LDAP protocol of a modify change, and requires you to set the `operation` and `modification`. Arguments: | Argument | Description | | ------------------------------------------ | ------------------------------------------- | | `operation` (replace|add|delete) | _See table below_ | | `modification` (Attribute) | Attribute details to add, remove, or update | Operations: | Value | Description | | --------- | --------------------------------------------------------------------------------------------------------------------- | | `replace` | Replaces the attribute referenced in `modification`. If the modification has no values, it is equivalent to a delete. | | `add` | Adds the attribute value(s) referenced in `modification`. The attribute may or may not already exist. | | `delete` | Deletes the attribute (and all values) referenced in `modification`. | ### modifyDN `modifyDN(dn, newDN, [controls])` Performs an LDAP modifyDN (rename) operation against an entry in the LDAP server. A couple points with this client API: - There is no ability to set "keep old dn." It's always going to flag the old dn to be purged. - The client code will automatically figure out if the request is a "new superior" request ("new superior" means move to a different part of the tree, as opposed to just renaming the leaf). Arguments: | Argument | Description | | ------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | `dn` (string) | The DN of the entry to rename | | `newDN` (string) | The new RDN to use assign to the entry. It may be the same as the current RDN if you only intend to move the entry beneath a new parent. If the new RDN includes any attribute values that aren’t already in the entry, the entry will be updated to include them. | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Example: ```ts await client.modifyDN('cn=foo, o=example', 'cn=bar'); ``` ### search `search(baseDN, options, [controls])` Performs a search operation against the LDAP server. The search operation is more complex than the other operations, so this one takes an `options` object for all the parameters. Arguments: | Argument | Description | | ------------------------------------- | ---------------------------------------------------------------- | | `baseDN` (string) | The base of the subtree in which the search is to be constrained | | `options` (object) | _See table below_ | | `[controls]` (Control|Control[]) | Optional `Control` object or array of `Control` objects | Options: | Attribute | Description | | ---------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------ | | [scope=sub] (string) |
  • `base` - Indicates that only the entry specified as the search base should be considered. None of its subordinates will be considered.
  • `one` - Indicates that only the immediate children of the entry specified as the search base should be considered. The base entry itself should not be considered, nor any descendants of the immediate children of the base entry.
  • `sub` - Indicates that the entry specified as the search base, and all of its subordinates to any depth, should be considered.
  • `children` - Indicates that the entry specified by the search base should not be considered, but all of its subordinates to any depth should be considered.
| | [filter=(objectclass=*)] (string|Filter) | The filter of the search request. It must conform to the LDAP filter syntax specified in RFC4515 | | [derefAliases=never] (string) |
  • `never` - Never dereferences entries, returns alias objects instead. The alias contains the reference to the real entry.
  • `always` - Always returns the referenced entries, not the alias object.
  • `search` - While searching subordinates of the base object, dereferences any alias within the search scope. Dereferenced objects become the bases of further search scopes where the Search operation is also applied by the server. The server should eliminate duplicate entries that arise due to alias dereferencing while searching.
  • `find` - Dereferences aliases in locating the base object of the search, but not when searching subordinates of the base object.
| | [returnAttributeValues=true] (boolean) | If true, attribute values should be included in the entries that are returned; otherwise entries that match the search criteria should be returned containing only the attribute descriptions for the attributes contained in that entry but should not include the values for those attributes. | | [sizeLimit=0] (number) | The maximum number of entries that should be returned from the search. A value of zero indicates no limit. Note that the server may also impose a size limit for the search operation, and in that case the smaller of the client-requested and server-imposed size limits will be enforced. | | [timeLimit=10] (number) | The maximum length of time, in seconds, that the server should spend processing the search. A value of zero indicates no limit. Note that the server may also impose a time limit for the search operation, and in that case the smaller of the client-requested and server-imposed time limits will be enforced. | | [paged=false] (boolean|SearchPageOptions) | Used to allow paging and specify the page size. Note that even with paged options the result will contain all of the search results. If you need to paginate over results have a look at to [searchPaginated](#searchpaginated) method. | | [attributes=] (string[]) | A set of attributes to request for inclusion in entries that match the search criteria and are returned to the client. If a specific set of attribute descriptions are listed, then only those attributes should be included in matching entries. The special value “_” indicates that all user attributes should be included in matching entries. The special value “+” indicates that all operational attributes should be included in matching entries. The special value “1.1” indicates that no attributes should be included in matching entries. Some servers may also support the ability to use the “@” symbol followed by an object class name (e.g., “@inetOrgPerson”) to request all attributes associated with that object class. If the set of attributes to request is empty, then the server should behave as if the value “_” was specified to request that all user attributes be included in entries that are returned. | | [explicitBufferAttributes=] (string[]) | List of explicit attribute names to return as Buffer objects | Example: ```ts const { searchEntries, searchReferences } = await client.search(searchDN, { filter: '(mail=peter.parker@marvel.com)', }); ``` Please see [Client tests](https://github.com/ldapts/ldapts/blob/master/tests/Client.tests.ts) for more search examples ### searchPaginated `search(baseDN, options, [controls])` Performs a search operation against the LDAP server and retrieve results in a paginated way. The searchPagination the same `options` with [search](#search) method but returns an iterator. Example: ```ts const paginator = client.searchPaginated('o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com', { filter: 'objectclass=*', paged: { pageSize: 10, }, }); let total = 0; for await (const searchResult of paginator) { total += searchResult.searchEntries.length; console.log(searchResult.searchEntries); } console.log(`total results: ${total}`); ``` #### Filter Strings The easiest way to write search filters is to write them compliant with RFC2254, which is "The string representation of LDAP search filters." Assuming you don't really want to read the RFC, search filters in LDAP are basically are a "tree" of attribute/value assertions, with the tree specified in prefix notation. For example, let's start simple, and build up a complicated filter. The most basic filter is equality, so let's assume you want to search for an attribute `email` with a value of `foo@bar.com`. The syntax would be: ```ts const filter = `(email=foo@bar.com)`; ``` ldapts requires all filters to be surrounded by '()' blocks. OK, that was easy. Let's now assume that you want to find all records where the email is actually just anything in the "@bar.com" domain and the location attribute is set to Seattle: ```ts const filter = `(&(email=*@bar.com)(l=Seattle))`; ``` Now our filter is actually three LDAP filters. We have an `and` filter (single amp `&`), an `equality` filter `(the l=Seattle)`, and a `substring` filter. Substrings are wildcard filters. They use `*` as the wildcard. You can put more than one wildcard for a given string. For example you could do `(email=*@*bar.com)` to match any email of @bar.com or its subdomains like "". This also means that any search filter syntax characters, such as the wildcard character `*` or parentheses, must be escaped (RFC2254 §4 _String Search Filter Definition_) in order to search for them and to prevent such characters in a string from an untrusted source to be misinterpreted as syntax characters (preventing injection attacks). The easiest way to do that is the `escapeFilter` tagged template literal, which escapes every interpolated value while leaving the filter syntax itself alone: ```ts import { escapeFilter } from 'ldapts'; const value = 'x*x@foo.net'; const filter = escapeFilter`(email=${value})`; ``` That'll create a search filter to search for an exact match of `x*x@foo.net`, not treating `*` as a wildcard character. Interpolated strings and Buffers are escaped; numbers and booleans are stringified. Individual values can also be escaped explicitly with the `Filter.escape()` utility function: ```ts import { Filter } from 'ldapts'; const value = 'x*x@foo.net'; const filter = `(email=${Filter.escape(value)})`; ``` Now, let's say we also want to set our filter to include a specification that either the employeeType _not_ be a manager nor a secretary: ```ts const filter = `(&(email=*@bar.com)(l=Seattle)(!(|(employeeType=manager)(employeeType=secretary))))`; ``` The `not` character is represented as a `!`, the `or` as a single pipe `|`. It gets a little bit complicated, but it's actually quite powerful, and lets you find almost anything you're looking for. #### Return buffer for specific attribute Sometimes you may want to get a buffer back instead of a string for an attribute value. Depending on the server software, you may be able to append `;binary` (the [binary attribute subtype](https://docs.oracle.com/cd/E19424-01/820-4809/bcacz/index.html)) to the attribute name, to have the value returned as a Buffer. ```ts const searchResults = await ldapClient.search('ou=Users,o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com', { filter: '(mail=peter.parker@marvel.com)', attributes: ['jpegPhoto;binary'], }); ``` However, some servers are very strict when it comes to the binary attribute subtype and will only acknowledge it if there is an associated AN.1 type or valid BER encoding. In those cases, you can tell ldapts to explicitly return a Buffer for an attribute: ```ts const searchResult = await client.search('ou=Users,o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com', { filter: '(mail=peter.parker@marvel.com)', explicitBufferAttributes: ['jpegPhoto'], }); ``` ### unbind `unbind()` Used to indicate that the client wants to close the connection to the directory server. Example: ```ts await client.unbind(); ``` ## Usage Examples ### Authenticate example ```ts const { Client } = require('ldapts'); const url = 'ldap://ldap.forumsys.com:389'; const bindDN = 'cn=read-only-admin,dc=example,dc=com'; const password = 'password'; const client = new Client({ url, }); let isAuthenticated; try { await client.bind(bindDN, password); isAuthenticated = true; } catch (ex) { isAuthenticated = false; } finally { await client.unbind(); } ``` ### Search example ```ts const { Client } = require('ldapts'); const url = 'ldaps://ldap.jumpcloud.com'; const bindDN = 'uid=tony.stark,ou=Users,o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com'; const password = 'MyRedSuitKeepsMeWarm'; const searchDN = 'ou=Users,o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com'; const client = new Client({ url, tlsOptions: { rejectUnauthorized: args.rejectUnauthorized, }, }); try { await client.bind(bindDN, password); const { searchEntries, searchReferences } = await client.search(searchDN, { scope: 'sub', filter: '(mail=peter.parker@marvel.com)', }); } catch (ex) { throw ex; } finally { await client.unbind(); } ``` ### Delete Active Directory entry example ```ts const { Client } = require('ldapts'); const url = 'ldap://127.0.0.1:1389'; const bindDN = 'uid=foo,dc=example,dc=com'; const password = 'bar'; const dnToDelete = 'uid=foobar,dc=example,dc=com'; const client = new Client({ url, }); try { await client.bind(bindDN, password); await client.del(dnToDelete); } catch (ex) { if (ex instanceof InvalidCredentialsError) { // Handle authentication specifically } throw ex; } finally { await client.unbind(); } ``` ## `using` declaration with TypeScript For more details look have a look at [using Declarations and Explicit Resource Management](https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-2.html) ```ts { await using client = new Client({ url: 'ldap://127.0.0.1:1389', }); await client.bind(bindDN, password); } // unbind is called ``` ## Configuring Secure Connections When configuring secure connections in `ldapts`, it is important to choose the appropriate TLS method (via the `Client` constructor or `client.startTLS`) based on your LDAP server's requirements. Prefer connecting securely from the Client constructor if the server supports LDAP over SSL. If that's not available but the connection can be upgraded to a secure connection, then connect insecurely and call startTLS to upgrade the connection to be secure. ### Use `Client` Constructor for LDAPS If the LDAP server requires `ldaps://` (LDAP over SSL), specify `ldaps://` in the `url` and provide `tlsOptions` to the Client constructor to handle certificate validation. This will establish a secure connection to the LDAP server over TLS from the moment the LDAP client is instantiated. Example: ```ts import { Client } from 'ldapts'; import fs from 'fs'; const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { ca: [fs.readFileSync('/path/to/ca-cert.pem')], }, }); ``` ### Use `client.startTLS` for STARTTLS If the server is unable to support LDAP over SSL but supports `STARTTLS`, the connection can be upgraded to a secure connection. Connect to the server using `ldap://` and call `client.startTLS()` to upgrade to a secure connection. Example: ```ts import { Client } from 'ldapts'; import fs from 'fs'; const client = new Client({ url: 'ldap://ldap.example.com' }); async function connectWithStartTLS() { await client.startTLS({ ca: [fs.readFileSync('/path/to/ca-cert.pem')], }); } ``` ## Common Errors ### Client network socket disconnected before secure TLS connection was established Cause: There is a mismatch between the LDAP protocol being used by the client and server. For example, The server expects an `ldaps://` connection, but the client uses `ldap://` without upgrading to TLS. Alternatively, the client is configured to use LDAP over SSL, but is connecting to the server using the `ldap://` protocol. Solution: Review the configuration options above and ensure that the client and server are configured to use the same protocol. If connecting to a `ldap://` address, call `startTLS()` to upgrade the connection. If connecting to an `ldaps://` protocol, provide the TLS options in the `Client` constructor. ### UNABLE_TO_VERIFY_LEAF_SIGNATURE Cause: The LDAP server's certificate is untrusted, often due to a self-signed certificate or an incomplete certificate chain. Solution(s): - Provide the CA certificate to establish trust: ```ts import fs from 'fs'; const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { ca: [fs.readFileSync('/path/to/ca-cert.pem')], }, }); ``` - Allow self-signed certificates (not recommended for production): ```ts const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { rejectUnauthorized: false, // Allow untrusted certificates }, }); ``` ### ECONNREFUSED Cause: The LDAP server is not reachable at the specified host and port, or the server is not listening on the expected protocol. Solution: - Verify the server address, port, and protocol in the `url`. - Ensure the server is running and accessible. - Check firewall rules to allow connections on the required port (636 for `ldaps`, 389 for `ldap`). ### Handshake inactivity timeout Cause: The server takes too long to respond to the TLS handshake due to network latency or misconfiguration. Solution: - Check the LDAP server's performance. - Verify TLS configuration (e.g., ciphers and protocols) on both client and server. - Increase the timeout if necessary: ```ts const client = new Client({ url: 'ldaps://ldap.example.com', timeout: 30000, // 30 seconds }); ``` ### Self signed certificate in certificate chain Cause: The server uses a certificate signed by an untrusted CA. Solution: Provide the CA certificate: ```ts import fs from 'fs'; const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { ca: [fs.readFileSync('/path/to/ca-cert.pem')], }, }); ``` ### DEPTH_ZERO_SELF_SIGNED_CERT Cause: The LDAP server is using a self-signed certificate without an intermediate CA. Solution(s): - Use the server's certificate as the trusted CA: ```ts import fs from 'fs'; const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { ca: [fs.readFileSync('/path/to/server-cert.pem')], }, }); ``` - Allow self-signed certificates (not recommended for production): ```ts const client = new Client({ url: 'ldaps://ldap.example.com', tlsOptions: { rejectUnauthorized: false, // Allow self-signed certificates }, }); ``` ## Development ### Generate certificates Run [generate-certs.js](tests/data/generate-certs.mjs) ```sh node tests/data/generate-certs.mjs ``` ### Start test OpenLDAP server ```shell docker compose up -d ``` ### Close test OpenLDAP server ```shell docker compose down ``` ldapts-ldapts-91d5f4e/docker-compose.yml000066400000000000000000000021651522446626000203610ustar00rootroot00000000000000services: openldap: image: osixia/openldap:1.5.0@sha256:18742e9c449c9c1afe129d3f2f3ee15fb34cc43e5f940a20f3399728f41d7c28 ports: - '389:389' - '636:636' command: --copy-service -l=debug volumes: - ./tests/data/ldif/:/container/service/slapd/assets/config/bootstrap/ldif/custom/ - ./tests/data/certs/:/container/service/slapd/assets/certs environment: KEEP_EXISTING_CONFIG: false LDAP_DOMAIN: ldap.local LDAP_TLS: true LDAP_TLS_ENFORCE: false LDAP_ADMIN_PASSWORD: 1234 LDAP_CONFIG_PASSWORD: 1234 LDAP_REMOVE_CONFIG_AFTER_SETUP: false LDAP_TLS_CRT_FILENAME: server.pem LDAP_TLS_KEY_FILENAME: server-key.pem LDAP_TLS_CA_CRT_FILENAME: ca.pem LDAP_TLS_VERIFY_CLIENT: allow LDAP_TLS_CIPHER_SUITE: 'NORMAL' healthcheck: test: ldapsearch -x -H ldaps://localhost:636 -b dc=ldap,dc=local -D "cn=admin,dc=ldap,dc=local" -w 1234 -s base objectclass=* || exit 1 interval: 60s retries: 60 start_period: 5s start_interval: 1s timeout: 3s extra_hosts: - 'host.docker.internal:host-gateway' ldapts-ldapts-91d5f4e/package.json000066400000000000000000000054431522446626000172140ustar00rootroot00000000000000{ "name": "ldapts", "version": "9.0.0", "description": "LDAP client", "keywords": [ "active", "directory", "ldap" ], "homepage": "https://github.com/ldapts/ldapts#readme", "bugs": { "url": "https://github.com/ldapts/ldapts/issues" }, "license": "MIT", "author": "jim@biacreations.com", "repository": { "type": "git", "url": "git+https://github.com/ldapts/ldapts.git" }, "files": [ "dist", "src" ], "type": "module", "main": "./dist/index.cjs", "module": "./dist/index.mjs", "types": "./dist/index.d.ts", "exports": { ".": { "import": { "types": "./dist/index.d.ts", "default": "./dist/index.mjs" }, "require": { "types": "./dist/index.d.cts", "default": "./dist/index.cjs" } } }, "scripts": { "build": "vp pack", "test": "pnpm run test:types && vp test run", "test:types": "tsc --noEmit --skipLibCheck", "test:imports": "node --no-experimental-require-module tests/import.tests.cjs && node tests/import.tests.mjs", "lint:format": "vp fmt", "lint:markdown": "markdownlint '*.md' '!(node_modules|dist)/**/*.md' --config=.github/linters/.markdown-lint.yml --fix", "lint:code": "vp lint --fix", "lint": "run-s lint:format lint:code lint:markdown", "lint-staged": "lint-staged", "docker:up": "docker compose up -d --wait", "docker:down": "docker compose down", "beta": "pnpm publish --tag beta", "prepublishOnly": "pinst --disable", "postpublish": "pinst --enable", "prepare": "husky" }, "dependencies": { "strict-event-emitter-types": "2.0.0" }, "devDependencies": { "@semantic-release/changelog": "6.0.3", "@semantic-release/commit-analyzer": "13.0.1", "@semantic-release/git": "10.0.1", "@semantic-release/github": "12.0.9", "@semantic-release/npm": "13.1.5", "@semantic-release/release-notes-generator": "14.1.1", "@stylistic/eslint-plugin": "5.10.0", "@types/node": ">=22", "eslint-config-decent": "4.2.38", "eslint-plugin-jsdoc": "62.9.0", "eslint-plugin-security": "4.0.1", "eslint-plugin-unicorn": "64.0.0", "husky": "9.1.7", "lint-staged": "17.0.8", "markdownlint-cli": "0.49.0", "node-forge": "1.4.0", "npm-run-all2": "9.0.2", "oxlint-plugin-eslint": "1.73.0", "pinst": "3.0.0", "semantic-release": "25.0.5", "typescript": "7.0.2", "vite": "catalog:", "vite-plus": "catalog:" }, "lint-staged": { "*.md": [ "vp fmt", "markdownlint --config=.github/linters/.markdown-lint.yml --fix" ], "*.{cjs,mjs,ts}": [ "vp fmt", "vp lint --fix" ], "*.{json,json5,yml,yaml}": [ "vp fmt --no-error-on-unmatched-pattern" ] }, "engines": { "node": ">=22" }, "packageManager": "pnpm@11.11.0" } ldapts-ldapts-91d5f4e/pnpm-lock.yaml000066400000000000000000007670121522446626000175210ustar00rootroot00000000000000lockfileVersion: '9.0' settings: autoInstallPeers: true excludeLinksFromLockfile: false catalogs: default: vite-plus: specifier: 0.2.4 version: 0.2.4 overrides: vite: npm:@voidzero-dev/vite-plus-core@0.2.4 importers: .: dependencies: strict-event-emitter-types: specifier: 2.0.0 version: 2.0.0 devDependencies: '@semantic-release/changelog': specifier: 6.0.3 version: 6.0.3(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/commit-analyzer': specifier: 13.0.1 version: 13.0.1(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/git': specifier: 10.0.1 version: 10.0.1(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/github': specifier: 12.0.9 version: 12.0.9(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/npm': specifier: 13.1.5 version: 13.1.5(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/release-notes-generator': specifier: 14.1.1 version: 14.1.1(semantic-release@25.0.5(typescript@7.0.2)) '@stylistic/eslint-plugin': specifier: 5.10.0 version: 5.10.0(eslint@10.6.0) '@types/node': specifier: '>=22' version: 26.1.1 eslint-config-decent: specifier: 4.2.38 version: 4.2.38(@stylistic/eslint-plugin@5.10.0(eslint@10.6.0))(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0)(typescript@7.0.2))(@typescript-eslint/utils@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint-plugin-jsdoc@62.9.0(eslint@10.6.0))(eslint-plugin-security@4.0.1)(eslint-plugin-unicorn@64.0.0(eslint@10.6.0))(eslint@10.6.0)(oxlint-plugin-eslint@1.73.0)(oxlint-tsgolint@0.24.0)(oxlint@1.72.0(oxlint-tsgolint@0.24.0)(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)))(prettier@3.9.5)(typescript@7.0.2) eslint-plugin-jsdoc: specifier: 62.9.0 version: 62.9.0(eslint@10.6.0) eslint-plugin-security: specifier: 4.0.1 version: 4.0.1 eslint-plugin-unicorn: specifier: 64.0.0 version: 64.0.0(eslint@10.6.0) husky: specifier: 9.1.7 version: 9.1.7 lint-staged: specifier: 17.0.8 version: 17.0.8 markdownlint-cli: specifier: 0.49.0 version: 0.49.0 node-forge: specifier: 1.4.0 version: 1.4.0 npm-run-all2: specifier: 9.0.2 version: 9.0.2 oxlint-plugin-eslint: specifier: 1.73.0 version: 1.73.0 pinst: specifier: 3.0.0 version: 3.0.0 semantic-release: specifier: 25.0.5 version: 25.0.5(typescript@7.0.2) typescript: specifier: 7.0.2 version: 7.0.2 vite: specifier: npm:@voidzero-dev/vite-plus-core@0.2.4 version: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)' vite-plus: specifier: 'catalog:' version: 0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0) packages: '@actions/core@3.0.1': resolution: {integrity: sha512-a6d/Nwahm9fliVGRhdhofo40HjHQasUPusmc7vBfyky+7Z+P2A1J68zyFVaNcEclc/Se+eO595oAr5nwEIoIUA==} '@actions/exec@3.0.0': resolution: {integrity: sha512-6xH/puSoNBXb72VPlZVm7vQ+svQpFyA96qdDBvhB8eNZOE8LtPf9L4oAsfzK/crCL8YZ+19fKYVnM63Sl+Xzlw==} '@actions/http-client@4.0.1': resolution: {integrity: sha512-+Nvd1ImaOZBSoPbsUtEhv+1z99H12xzncCkz0a3RuehINE81FZSe2QTj3uvAPTcJX/SCzUQHQ0D1GrPMbrPitg==} '@actions/io@3.0.2': resolution: {integrity: sha512-nRBchcMM+QK1pdjO7/idu86rbJI5YHUKCvKs0KxnSYbVe3F51UfGxuZX4Qy/fWlp6l7gWFwIkrOzN+oUK03kfw==} '@babel/code-frame@7.29.7': resolution: {integrity: sha512-Aup7aUOfpbAUg2ROOJN6Iw5f9DMBlzu0mIkm/malLQFN/YQgO48wCj0Kxa3sEHJvPVFg7siR+qRInwXd2qhQKw==} engines: {node: '>=6.9.0'} '@babel/compat-data@7.29.7': resolution: {integrity: sha512-locTkQyKvwIEgBzVrn8693ebc97F2U8ZHjbXwDXJ5Fn2TCpNwTlKcaKLkdHop5c/icOFE7qt7Q9JC5hnKNa6Gg==} engines: {node: '>=6.9.0'} '@babel/core@7.29.7': resolution: {integrity: sha512-RgHBCvtjbOK2gXSNBNIkNoEc9qoVEtau3hj8gEqKQuL3HZAibKarWFEI3Lfm6EYKkLalOh8eSrj9b+ch9H/VBA==} engines: {node: '>=6.9.0'} '@babel/generator@7.29.7': resolution: {integrity: sha512-DkXD5OJQaAQIdZ1bt3UZdEnHAn9Imd3IVBdX03UFe+ony9Ojw5pzr9YVKGDY1jt+Gcn/FnGkNf8r+Vj5NOJWtQ==} engines: {node: '>=6.9.0'} '@babel/helper-compilation-targets@7.29.7': resolution: {integrity: sha512-wem6WaBj4NaVYVdNhLPPVacES6ZJ+KBBfSkTMD3YZxbP3rm3Di85tJU5ljaUNhaOynt+Aj0xruhYuzQBt8n71g==} engines: {node: '>=6.9.0'} '@babel/helper-globals@7.29.7': resolution: {integrity: sha512-3nQVUAtvkKH9zahfWgw96Jc/uFOmjACE1kQz82E2lqWmHBgjzbNlsC22nuQTfahmWeQtTq5nQ/4Nnd2A1wj4zA==} engines: {node: '>=6.9.0'} '@babel/helper-module-imports@7.29.7': resolution: {integrity: sha512-ejHwrQQYcm9xnTivShn2IDOlIzInN34AXskvq9QicvCtEzq1Vzclu/tKF8Jq1Cg8JG2GL6/EmjgsCT7lXepE3g==} engines: {node: '>=6.9.0'} '@babel/helper-module-transforms@7.29.7': resolution: {integrity: sha512-UPUVSyXbOh627KiCIGQSgwWzGeBKLkaJ9PJEdrngIwMSzxLR4jS4+f1f1jb7VzBbg8nFLaYotvVPFCTqdrmTAg==} engines: {node: '>=6.9.0'} peerDependencies: '@babel/core': ^7.0.0 '@babel/helper-string-parser@7.29.7': resolution: {integrity: sha512-Pb5ijPrZ89GDH8223L4UP8i6QApWxs04RbPQJTeWDV0/keR2E36MeKnyr6LYmUUvqRRI+Iv87SuF1W6ErINzYw==} engines: {node: '>=6.9.0'} '@babel/helper-validator-identifier@7.29.7': resolution: {integrity: sha512-qehxGkRj55h/ff8EMaJ+cYhyaKlHIxqYDn682wQD7RNp9UujOQsHog2uS0r2vzr4pW+sXf90NeeayjcNaX3fFg==} engines: {node: '>=6.9.0'} '@babel/helper-validator-option@7.29.7': resolution: {integrity: sha512-N9ZErrD+yW5geCDtBqnOoxmR8+tNKiGuxKlDpuJxfsqpa2dFcexaziGAE/qoHLiDDreVNMupxGmSoNlyvsA3gw==} engines: {node: '>=6.9.0'} '@babel/helpers@7.29.7': resolution: {integrity: sha512-1k2lAGRMfHTcwuNYcCNUmaUffmQv8KWMfh2iJUUeRlwlwH4FdNG7mfPI10NPfLHJFThE4Tyr4mv7kTNZOiPuBg==} engines: {node: '>=6.9.0'} '@babel/parser@7.29.7': resolution: {integrity: sha512-hnORnjP/1P/zFEndoeX+n+t1RwWRJiJpM/jO7FW32Kn9r5+sJB2JWOdYo4L6k78j15eCwY3Gm/7364B1EMwtNg==} engines: {node: '>=6.0.0'} hasBin: true '@babel/runtime@7.29.7': resolution: {integrity: sha512-Nq8OhGWiZIZGV6hLHoyAKLLcJihP/xFeBMGJoUrxTX2psI8dCifzLhZISFb+VWS3wFMRDmCGw5R+dOySCqPLhw==} engines: {node: '>=6.9.0'} '@babel/template@7.29.7': resolution: {integrity: sha512-puq+Gf35oI24FeN11LkoUQFqv9uwNeWpxXZi/Ji3rRIoKAzKnxRaZ+Gkj0vKS9ZCiTESfng1N9LyOyXvo+m+Gg==} engines: {node: '>=6.9.0'} '@babel/traverse@7.29.7': resolution: {integrity: sha512-EhlfNQtZ+NK22w5BM61ciuiq1m58ed33Wr1Xan//ZRTy6hgjnwyCffRYwzsGXdASJSUJ1guZILsErh1eQcl+zw==} engines: {node: '>=6.9.0'} '@babel/types@7.29.7': resolution: {integrity: sha512-4zBIxpPzowiZpusoFkyGVwakdRJUyuH5PxQ/PrqghfdFWWasvnCdPfQXHrenDai+gyLARulZjZowCOj6fjT4pA==} engines: {node: '>=6.9.0'} '@blazediff/core@1.9.1': resolution: {integrity: sha512-ehg3jIkYKulZh+8om/O25vkvSsXXwC+skXmyA87FFx6A/45eqOkZsBltMw/TVteb0mloiGT8oGRTcjRAz66zaA==} '@colors/colors@1.5.0': resolution: {integrity: sha512-ooWCrlZP11i8GImSjTHYHLkvFDP48nS4+204nGb1RiX/WXYHmJA2III9/e2DWVabCESdW7hBAEzHRqUn9OUVvQ==} engines: {node: '>=0.1.90'} '@emnapi/core@1.10.0': resolution: {integrity: sha512-yq6OkJ4p82CAfPl0u9mQebQHKPJkY7WrIuk205cTYnYe+k2Z8YBh11FrbRG/H6ihirqcacOgl2BIO8oyMQLeXw==} '@emnapi/runtime@1.10.0': resolution: {integrity: sha512-ewvYlk86xUoGI0zQRNq/mC+16R1QeDlKQy21Ki3oSYXNgLb45GV1P6A0M+/s6nyCuNDqe5VpaY84BzXGwVbwFA==} '@emnapi/wasi-threads@1.2.1': resolution: {integrity: sha512-uTII7OYF+/Mes/MrcIOYp5yOtSMLBWSIoLPpcgwipoiKbli6k322tcoFsxoIIxPDqW01SQGAgko4EzZi2BNv2w==} '@es-joy/jsdoccomment@0.86.0': resolution: {integrity: sha512-ukZmRQ81WiTpDWO6D/cTBM7XbrNtutHKvAVnZN/8pldAwLoJArGOvkNyxPTBGsPjsoaQBJxlH+tE2TNA/92Qgw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@es-joy/resolve.exports@1.2.0': resolution: {integrity: sha512-Q9hjxWI5xBM+qW2enxfe8wDKdFWMfd0Z29k5ZJnuBqD/CasY5Zryj09aCA6owbGATWz+39p5uIdaHXpopOcG8g==} engines: {node: '>=10'} '@eslint-community/eslint-utils@4.9.1': resolution: {integrity: sha512-phrYmNiYppR7znFEdqgfWHXR6NCkZEK7hwWDHZUjit/2/U0r6XvkDl0SYnoM51Hq7FhCGdLDT6zxCCOY1hexsQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} peerDependencies: eslint: ^6.0.0 || ^7.0.0 || >=8.0.0 '@eslint-community/regexpp@4.12.2': resolution: {integrity: sha512-EriSTlt5OC9/7SXkRSCAhfSxxoSUgBm33OH+IkwbdpgoqsSsUg7y3uh+IICI/Qg4BBWr3U2i39RpmycbxMq4ew==} engines: {node: ^12.0.0 || ^14.0.0 || >=16.0.0} '@eslint/compat@2.0.5': resolution: {integrity: sha512-IbHDbHJfkVNv6xjlET8AIVo/K1NQt7YT4Rp6ok/clyBGcpRx1l6gv0Rq3vBvYfPJIZt6ODf66Zq08FJNDpnzgg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: ^8.40 || 9 || 10 peerDependenciesMeta: eslint: optional: true '@eslint/config-array@0.23.5': resolution: {integrity: sha512-Y3kKLvC1dvTOT+oGlqNQ1XLqK6D1HU2YXPc52NmAlJZbMMWDzGYXMiPRJ8TYD39muD/OTjlZmNJ4ib7dvSrMBA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/config-helpers@0.6.0': resolution: {integrity: sha512-ii6Bw9jJ2zi2cWA2Z+9/QZ/+3DX6kwaV5Q986D/CdP3Lap3w/pgQZ373FV7byY/i7L4IRH/G43I5dz1ClsCbpA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/core@1.2.1': resolution: {integrity: sha512-MwcE1P+AZ4C6DWlpin/OmOA54mmIZ/+xZuJiQd4SyB29oAJjN30UW9wkKNptW2ctp4cEsvhlLY/CsQ1uoHDloQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/js@10.0.1': resolution: {integrity: sha512-zeR9k5pd4gxjZ0abRoIaxdc7I3nDktoXZk2qOv9gCNWx3mVwEn32VRhyLaRsDiJjTs0xq/T8mfPtyuXu7GWBcA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: ^10.0.0 peerDependenciesMeta: eslint: optional: true '@eslint/object-schema@3.0.5': resolution: {integrity: sha512-vqTaUEgxzm+YDSdElad6PiRoX4t8VGDjCtt05zn4nU810UIx/uNEV7/lZJ6KwFThKZOzOxzXy48da+No7HZaMw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@eslint/plugin-kit@0.7.2': resolution: {integrity: sha512-+CNAzxglkrpNf/kKywqQfk74QjtceuOE7Qm+AF8miRvPF/wmmK5+OJOgVh3AVTT3RP2mH3+FOaxlE5v72owk0A==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} '@humanfs/core@0.19.2': resolution: {integrity: sha512-UhXNm+CFMWcbChXywFwkmhqjs3PRCmcSa/hfBgLIb7oQ5HNb1wS0icWsGtSAUNgefHeI+eBrA8I1fxmbHsGdvA==} engines: {node: '>=18.18.0'} '@humanfs/node@0.16.8': resolution: {integrity: sha512-gE1eQNZ3R++kTzFUpdGlpmy8kDZD/MLyHqDwqjkVQI0JMdI1D51sy1H958PNXYkM2rAac7e5/CnIKZrHtPh3BQ==} engines: {node: '>=18.18.0'} '@humanfs/types@0.15.0': resolution: {integrity: sha512-ZZ1w0aoQkwuUuC7Yf+7sdeaNfqQiiLcSRbfI08oAxqLtpXQr9AIVX7Ay7HLDuiLYAaFPu8oBYNq/QIi9URHJ3Q==} engines: {node: '>=18.18.0'} '@humanwhocodes/module-importer@1.0.1': resolution: {integrity: sha512-bxveV4V8v5Yb4ncFTT3rPSgZBOpCkjfK0y4oVVVJwIuDVBRMDXrPyXRL988i5ap9m9bnyEEjWfm5WkBmtffLfA==} engines: {node: '>=12.22'} '@humanwhocodes/retry@0.4.3': resolution: {integrity: sha512-bV0Tgo9K4hfPCek+aMAn81RppFKv2ySDQeMoSZuvTASywNTnVJCArCZE2FWqpvIatKu7VMRLWlR1EazvVhDyhQ==} engines: {node: '>=18.18'} '@jridgewell/gen-mapping@0.3.13': resolution: {integrity: sha512-2kkt/7niJ6MgEPxF0bYdQ6etZaA+fQvDcLKckhy1yIQOzaoKjBBjSj63/aLVjYE3qhRt5dvM+uUyfCg6UKCBbA==} '@jridgewell/remapping@2.3.5': resolution: {integrity: sha512-LI9u/+laYG4Ds1TDKSJW2YPrIlcVYOwi2fUC6xB43lueCjgxV4lffOCZCtYFiH6TNOX+tQKXx97T4IKHbhyHEQ==} '@jridgewell/resolve-uri@3.1.2': resolution: {integrity: sha512-bRISgCIjP20/tbWSPWMEi54QVPRZExkuD9lJL+UIxUKtwVJA8wW1Trb1jMs1RFXo1CBTNZ/5hpC9QvmKWdopKw==} engines: {node: '>=6.0.0'} '@jridgewell/sourcemap-codec@1.5.5': resolution: {integrity: sha512-cYQ9310grqxueWbl+WuIUIaiUaDcj7WOq5fVhEljNVgRfOUhY9fy2zTvfoqWsnebh8Sl70VScFbICvJnLKB0Og==} '@jridgewell/trace-mapping@0.3.31': resolution: {integrity: sha512-zzNR+SdQSDJzc8joaeP8QQoCQr8NuYx2dIIytl1QeBEZHJ9uW6hebsrYgbz8hJwUQao3TWCMtmfV8Nu1twOLAw==} '@napi-rs/wasm-runtime@1.1.6': resolution: {integrity: sha512-ZLv/JdUfkvOy9eCnnBaGfiO+XimbjebAeO+MRQqD/B+FR1tnRN0tpKSJHRbE8sFfS6aqsXZ67TQjfwfsxULVbg==} peerDependencies: '@emnapi/core': ^1.7.1 '@emnapi/runtime': ^1.7.1 '@next/eslint-plugin-next@16.2.4': resolution: {integrity: sha512-tOX826JJ96gYK/go18sPUgMq9FK1tqxBFfUCEufJb5XIkWFFmpgU7mahJANKGkHs7F41ir3tReJ3Lv5La0RvhA==} '@nodelib/fs.scandir@2.1.5': resolution: {integrity: sha512-vq24Bq3ym5HEQm2NKCr3yXDwjc7vTsEThRDnkp2DK9p1uqLR+DHurm/NOTo0KG7HYHU7eppKZj3MyqYuMBf62g==} engines: {node: '>= 8'} '@nodelib/fs.stat@2.0.5': resolution: {integrity: sha512-RkhPPp2zrqDAQA/2jNhnztcPAlv64XdhIp7a7454A5ovI7Bukxgt7MX7udwAu3zg1DcpPU0rz3VV1SeaqvY4+A==} engines: {node: '>= 8'} '@nodelib/fs.walk@1.2.8': resolution: {integrity: sha512-oGB+UxlgWcgQkgwo8GcEGwemoTFt3FIO9ababBmaGwXIoBKZ+GTy0pP185beGg7Llih/NSHSV2XAs1lnznocSg==} engines: {node: '>= 8'} '@octokit/auth-token@6.0.0': resolution: {integrity: sha512-P4YJBPdPSpWTQ1NU4XYdvHvXJJDxM6YwpS0FZHRgP7YFkdVxsWcpWGy/NVqlAA7PcPCnMacXlRm1y2PFZRWL/w==} engines: {node: '>= 20'} '@octokit/core@7.0.6': resolution: {integrity: sha512-DhGl4xMVFGVIyMwswXeyzdL4uXD5OGILGX5N8Y+f6W7LhC1Ze2poSNrkF/fedpVDHEEZ+PHFW0vL14I+mm8K3Q==} engines: {node: '>= 20'} '@octokit/endpoint@11.0.3': resolution: {integrity: sha512-FWFlNxghg4HrXkD3ifYbS/IdL/mDHjh9QcsNyhQjN8dplUoZbejsdpmuqdA76nxj2xoWPs7p8uX2SNr9rYu0Ag==} engines: {node: '>= 20'} '@octokit/graphql@9.0.3': resolution: {integrity: sha512-grAEuupr/C1rALFnXTv6ZQhFuL1D8G5y8CN04RgrO4FIPMrtm+mcZzFG7dcBm+nq+1ppNixu+Jd78aeJOYxlGA==} engines: {node: '>= 20'} '@octokit/openapi-types@27.0.0': resolution: {integrity: sha512-whrdktVs1h6gtR+09+QsNk2+FO+49j6ga1c55YZudfEG+oKJVvJLQi3zkOm5JjiUXAagWK2tI2kTGKJ2Ys7MGA==} '@octokit/plugin-paginate-rest@14.0.0': resolution: {integrity: sha512-fNVRE7ufJiAA3XUrha2omTA39M6IXIc6GIZLvlbsm8QOQCYvpq/LkMNGyFlB1d8hTDzsAXa3OKtybdMAYsV/fw==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=6' '@octokit/plugin-retry@8.1.0': resolution: {integrity: sha512-O1FZgXeiGb2sowEr/hYTr6YunGdSAFWnr2fyW39Ah85H8O33ELASQxcvOFF5LE6Tjekcyu2ms4qAzJVhSaJxTw==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': '>=7' '@octokit/plugin-throttling@11.0.3': resolution: {integrity: sha512-34eE0RkFCKycLl2D2kq7W+LovheM/ex3AwZCYN8udpi6bxsyjZidb2McXs69hZhLmJlDqTSP8cH+jSRpiaijBg==} engines: {node: '>= 20'} peerDependencies: '@octokit/core': ^7.0.0 '@octokit/request-error@7.1.0': resolution: {integrity: sha512-KMQIfq5sOPpkQYajXHwnhjCC0slzCNScLHs9JafXc4RAJI+9f+jNDlBNaIMTvazOPLgb4BnlhGJOTbnN0wIjPw==} engines: {node: '>= 20'} '@octokit/request@10.0.11': resolution: {integrity: sha512-+s7HUxjfFqOMS9VlIwDffq0MikjSAK0gSpG73W+meAvVAvX4MBrHYTK5Bj3Uot55qFT4gzUtfzE4mGWY4Br8/Q==} engines: {node: '>= 20'} '@octokit/types@16.0.0': resolution: {integrity: sha512-sKq+9r1Mm4efXW1FCk7hFSeJo4QKreL/tTbR0rz/qx/r1Oa2VV83LTA/H/MuCOX7uCIJmQVRKBcbmWoySjAnSg==} '@oxc-project/runtime@0.138.0': resolution: {integrity: sha512-yHhoXsN8tYxgdJCdD91PbySNjEEaBX/tH2OQRDXJpsQv5b184oC4/qVbU7qlblvfil/JP15Lh2HW7+HN5DS90Q==} engines: {node: ^20.19.0 || >=22.12.0} '@oxc-project/types@0.138.0': resolution: {integrity: sha512-1a7ZKmrRTCoN1XMZ4L0PyyqrMnrNlLyPuOkdSX2MZg7IiIGRUyurNhAm73ptDOraoBcIordsIGKNPKUzy3ZmfA==} '@oxfmt/binding-android-arm-eabi@0.57.0': resolution: {integrity: sha512-qVBsEO+KugOsCmUHcO8iqNnqc65p7PCKpCs8M66mPZ+Ri+CWbcpoQOEJBg2OTu03+0qu++NK1jj6IzvQVs0Sig==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxfmt/binding-android-arm64@0.57.0': resolution: {integrity: sha512-mp6PibWbao3aizijcheOeHQaYEhcUAt8pwLniYbtLfHxL/psFF0BykAwCj+s3c6qIpa8yN8keZICWrqtZ70w8g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxfmt/binding-darwin-arm64@0.57.0': resolution: {integrity: sha512-T+0stuCBqmUVY+aMIvrgXhzGhHO3sD5tNiiEcYqgSdPsnukskQqn2u5qOVD0sv1l7RLdFS5Z/f5Wi9Ktyjr3Eg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxfmt/binding-darwin-x64@0.57.0': resolution: {integrity: sha512-O+3JbqWs/mCI2oi4xfhRO2IVPFJNDDEBV8Odo+ZpmsUOeKJfjXoNH7nDmBEQcDgK7NfjDIyE7kRgYSZcTLDO0A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxfmt/binding-freebsd-x64@0.57.0': resolution: {integrity: sha512-pxwhxVC+JkLX9twOQ/8C/vbuOQcMZyKIDmiRDZfO7yITuVcIdZCiLRqqf4QOxb2+8FWrRXzQpm+1DBKcMpHSSQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': resolution: {integrity: sha512-pxBU4zH2imB/MDBfth2rOMeVxXUMjRQLCazagwLARIFH3hVlxZJBlM4nSnHXaIHJK4/qezoFCIORN6AY8Mra4A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm-musleabihf@0.57.0': resolution: {integrity: sha512-JAprOzt8tycYou36ZgEw14DlRHTiN8qdtKANdV3VZIRIvTI/lh/cX13c9pJ/EnDk2GT3FASH7KvCgQ2AufAifQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxfmt/binding-linux-arm64-gnu@0.57.0': resolution: {integrity: sha512-ajtjaxSaj9xl4BW7REt+Cef/ttzbAq00Bq4z7JUDZEfgFXdwSjH8K9bF+IcIJzZB9lKqMfQ4eHuSFOvvlvtqOg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@oxfmt/binding-linux-arm64-musl@0.57.0': resolution: {integrity: sha512-p4Y/+RYk9Bk5WO+zHSUXAClRmZ2fbJCejMuCAsU2HhyME4jqf6Ftt/mJYEwIah1wGCBDYOB7wEGV1x5bCEZ6hA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@oxfmt/binding-linux-ppc64-gnu@0.57.0': resolution: {integrity: sha512-By6tRALAZsno0F4zedmtG+wdMvJiJmJoXM4d3+A9zHE4HRXLqXITwRH8mgrlcXc5yJM2g2W3riRPwTYdgemZLQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@oxfmt/binding-linux-riscv64-gnu@0.57.0': resolution: {integrity: sha512-skYeG+RgvyzspqVEBsEprL90OYYZfoVNqB3HcCNR6QDJyXKOzfDRT3zncnHmUaFluIlBHuY23mU1b5WGgR98hA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] '@oxfmt/binding-linux-riscv64-musl@0.57.0': resolution: {integrity: sha512-FFgACrZOXAXUh5KQh2mt1CDOVOZmn+QzHP71wM9QobNwyQvoFfyAeefVUltW83g3sm7LTiH3yfFqLLVUpA5ZFQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] '@oxfmt/binding-linux-s390x-gnu@0.57.0': resolution: {integrity: sha512-Nm/BAOfQeFiiKd502mZn/GAVKJwtd0RdCg17G3Wz/WSOIQmDi3+7/SZH4BHn1Ye5KvTVH3ua8WvfwLLycNIuvA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@oxfmt/binding-linux-x64-gnu@0.57.0': resolution: {integrity: sha512-BiSy5Ku3mQqyxS6YIqAJgd403wEUWvI7kerfzPxc2l/txZVmZM0pSj7oDM+4bGBExowxOi7o73jEam1W0EDTZg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@oxfmt/binding-linux-x64-musl@0.57.0': resolution: {integrity: sha512-BCRkJiotz5s9afLYD2LuMvzAoDYx9H17E/YbDyu4xK7l4zHDPeny9ErSXL//i/nJyaOwRk08x4b8cgJC00+JDg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@oxfmt/binding-openharmony-arm64@0.57.0': resolution: {integrity: sha512-4Oaxe1qrGgXfpCJ1C/ERJ2iCtV2rN1R79ga9fsfyVHfSQRu/hVW780u2KDqZWFZ/iGTHODJji0JemxqFZ63eIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxfmt/binding-win32-arm64-msvc@0.57.0': resolution: {integrity: sha512-MYLAsDnhdNsSGheLYhWgbk0vfIrlS84iQYun/y21fX6u0jj8iBtYtbpZMdiqYeuf8U12eVPUjVY2xE2NrCfJ0g==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxfmt/binding-win32-ia32-msvc@0.57.0': resolution: {integrity: sha512-PBwdzZALJY/jcCx2E6is0yu+cuVXeySTDmwuseD+9j0mHqlRNxwlKgsyRTBed/woPeqfVfuXfWjoq4Cx2Zt3Eg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxfmt/binding-win32-x64-msvc@0.57.0': resolution: {integrity: sha512-bQJdH9i4RRfw55jm7+8/xS7GzHLLTbHx4huhrrDxQJaJtbSDbsyOnODvP1ftT7EG0KFKAYO2S+q6AcioXODx8w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxlint-tsgolint/darwin-arm64@0.24.0': resolution: {integrity: sha512-C2uMmwK5Bc4ri4ysZ6sA8Rcu+A5zBQTp6ml2u0CLLbRZp4kMFPV3yWk8B5DK9Aw7y9bbjogIm75tUwGLFzlsYQ==} cpu: [arm64] os: [darwin] '@oxlint-tsgolint/darwin-x64@0.24.0': resolution: {integrity: sha512-Wgvt/1lRbDxmoNqWQKKcL+UIiqLmdJ+EWLpQa1qzoNVAfNB0PJpa82/8dH1twT/3rSs4zrP5TXPWl4juB71WuQ==} cpu: [x64] os: [darwin] '@oxlint-tsgolint/linux-arm64@0.24.0': resolution: {integrity: sha512-PB1rxII7KV83+ASY4sSkXtqvpij6ME66+QCRL49uksi/ofs2Rf/UVboYr095n0Rkbl2wgvlsHGl6DHC361jQUQ==} cpu: [arm64] os: [linux] '@oxlint-tsgolint/linux-x64@0.24.0': resolution: {integrity: sha512-xcz3CxKmjTQLREtE/UShh+ruWmm9nAb7UM9zKcD65BStiuYgOakAKkPHl4YS5DztpVcDrE0+HqbOolTlRKYWmw==} cpu: [x64] os: [linux] '@oxlint-tsgolint/win32-arm64@0.24.0': resolution: {integrity: sha512-A2i6ZGBec3i20S7RaxkgHc6r3HYtD5Mn7j/mb22NkTz14u0JuudvTu6JggAnbGMcv8+dBKQI//EasxSPJLD8pw==} cpu: [arm64] os: [win32] '@oxlint-tsgolint/win32-x64@0.24.0': resolution: {integrity: sha512-0ZbGd9qRB6zs82moekaKdEvncRANq49EAwfNX62JpTS46feXUhKAuoyVDvZMj6Rywejylrmmu79Wo6faYCo4Ew==} cpu: [x64] os: [win32] '@oxlint/binding-android-arm-eabi@1.72.0': resolution: {integrity: sha512-zhCmvn+1Mj3UchAc/90i99S0t7jJUsHmFVSPg4UWrjO8b8eaSGwscgO6QAUtvHBstkjQwBttQNswEnAF1mIQdA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [android] '@oxlint/binding-android-arm64@1.72.0': resolution: {integrity: sha512-mtH+aY/ozv1eZoCUC2owjFAtyNBKHpJHygKeEu9zXXnQGW1Q2/qOpvx+I+Lf23+TvTz66F4iiXUbl2cGvoLPCQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [android] '@oxlint/binding-darwin-arm64@1.72.0': resolution: {integrity: sha512-EvnajNPDtfknB3ZieeOOyDTwJn9QXDiwfnF4ZDQqART6RG6hjY4WigQcZdGoK2dkB3e1vrmEzN9aYbQCUkh/gQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [darwin] '@oxlint/binding-darwin-x64@1.72.0': resolution: {integrity: sha512-ZkCdEa/G80A7vEHfeCDz/+L3m33DE73v32mDKhgOIgz8Uwf0DFcK7+uu6qC+7LEhmz5fpOe1osWKyjSNMydFIQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [darwin] '@oxlint/binding-freebsd-x64@1.72.0': resolution: {integrity: sha512-NroXv2vh+sxVY1uya/rM5pjhx1hm8BzlYpx9q67QP0Xhw5MH2bf5GJylpvLEC+781p1Xli/317EoV9AlGwViag==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [freebsd] '@oxlint/binding-linux-arm-gnueabihf@1.72.0': resolution: {integrity: sha512-0NDywYgfj279Ou/BcQuCYSj7NJwBfmWn5qc5uGO/Ny7fUWmXyIpvawqX/8acQlWG6IXelJsJhj+JAy6sjsKj0A==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm-musleabihf@1.72.0': resolution: {integrity: sha512-4vpXB06h65Ezsy4hRyrGjGrfa1SkVPii09yaajiYhmVpgsFiLD+KNxIx/BNAY+XiO+i1yqp9HHdwqM8VTqa5XQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm] os: [linux] '@oxlint/binding-linux-arm64-gnu@1.72.0': resolution: {integrity: sha512-immaN4g2ZGFiOkKrvRX9LvzZdd2GkQM5wR+UyzYyUuyhUTXGQ4HKUJH18xp4G8OfhCVaVAJfKZxwE1r8+4hhaQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [glibc] '@oxlint/binding-linux-arm64-musl@1.72.0': resolution: {integrity: sha512-JGHS9Mnr7iWyyLDxgCv1MhzVpAckgptg00F2gnxt/GD7lQ2SW1BRcxHqhSTaSdDpjWRrBkBxMMh4+Hn3aVtExg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [linux] libc: [musl] '@oxlint/binding-linux-ppc64-gnu@1.72.0': resolution: {integrity: sha512-AOYgBZqxNshrg83P9v0RYv+m8s10Cqkj4/PxXFDhcS3k7FqsIG5+CxErshZCIN7G8iy4Y+VGfAsuEdar8AcbBg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ppc64] os: [linux] libc: [glibc] '@oxlint/binding-linux-riscv64-gnu@1.72.0': resolution: {integrity: sha512-QMybPS5ij3/vrKG67mqzHwW++91sYxK/PPUVi6SBtNCEzW4niS52fVBdXbQ6nou0wWbUPEpx8Sl/ZjtgE3clXA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [glibc] '@oxlint/binding-linux-riscv64-musl@1.72.0': resolution: {integrity: sha512-gOc3W7JV0PXRpIL7stUlLe3Wa9Gp0Kdlup87IT3gHDvPKck2xNgMIl/Gs2lldYY2lyXZDC4rWi3hmoLUobkgbQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [riscv64] os: [linux] libc: [musl] '@oxlint/binding-linux-s390x-gnu@1.72.0': resolution: {integrity: sha512-rpGxph+FjjHcYI5q6uxB3Az+tnfmEnDbSA8+PK9ZE/VzyUAkvBOMeuY7ZQMhu5mpZH7YQDsTdW6Cx4kV/msc6w==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [s390x] os: [linux] libc: [glibc] '@oxlint/binding-linux-x64-gnu@1.72.0': resolution: {integrity: sha512-WND+uhf/Ko13SLqQMWQUgsZuLvYYEvL0ZKgg0tgGYfLqxG7l8Ju123fHDMJyYSDl5E3bUbpFUuii/OvMreFQzw==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [glibc] '@oxlint/binding-linux-x64-musl@1.72.0': resolution: {integrity: sha512-SrpbrUL70nG9vh6zP4/oKHWgLuHquwsr7MW9XOn0olBVgh10Uqr8qscKhQoBGEn6olK/IUpn5GSKcdQ5AjUhGA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [linux] libc: [musl] '@oxlint/binding-openharmony-arm64@1.72.0': resolution: {integrity: sha512-qkrsEn6NmgFKr7U/QnezQMb+q/vzAy0Dd9Y95gQGQTyjzDLN+HRZMuM5u70iyH4nBLCfKBzhjMsYCehKay2jyg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [openharmony] '@oxlint/binding-win32-arm64-msvc@1.72.0': resolution: {integrity: sha512-LWR6ZlFZph+KPjXv8opgZsXRDCdrdQe8VL8Cg9zxCoBS73h6znzZpydVgmdnwj8mB9AuSM5jxEgDJDpQkjboeg==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [arm64] os: [win32] '@oxlint/binding-win32-ia32-msvc@1.72.0': resolution: {integrity: sha512-yt6HEh7IsHvtjRWtmeZRX134eaXKHq5Gnqlf1xBJdJl1JtdoRUEJw3nAxpZoUDS860cX/foKbztO441anVBtVQ==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [ia32] os: [win32] '@oxlint/binding-win32-x64-msvc@1.72.0': resolution: {integrity: sha512-b2eKFD2hX7tIwmo/cyH6TDq8vzWRZ2qNHrzoGntUTmq0h3zQh/uX3eTSHCwI8OB/ADQfJCRelLItK8BsxuucDA==} engines: {node: ^20.19.0 || >=22.12.0} cpu: [x64] os: [win32] '@oxlint/plugins@1.68.0': resolution: {integrity: sha512-titLmukUt/h8ho7Svlf0xSBjoy2ccZKrXjpXpZCj+v6V4CJccC2KyP45BLSCMx8YIpifMyiDyUptM4+5sruKbQ==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} '@package-json/types@0.0.12': resolution: {integrity: sha512-uu43FGU34B5VM9mCNjXCwLaGHYjXdNincqKLaraaCW+7S2+SmiBg1Nv8bPnmschrIfZmfKNY9f3fC376MRrObw==} '@pkgr/core@0.3.6': resolution: {integrity: sha512-SEeaJLb3qBNF/OaXnaR1NmmBbFYk1zC0ZH/52fATcRPLFg/p791YrcyFFy44Bo9sLaGuSuLp5Q6axbb/O+v/RA==} engines: {node: ^14.18.0 || >=16.0.0} '@pnpm/config.env-replace@1.1.0': resolution: {integrity: sha512-htyl8TWnKL7K/ESFa1oW2UB5lVDxuF5DpM7tBi6Hu2LNL3mWkIzNLG6N4zoCUP1lCKNxWy/3iu8mS8MvToGd6w==} engines: {node: '>=12.22.0'} '@pnpm/network.ca-file@1.0.2': resolution: {integrity: sha512-YcPQ8a0jwYU9bTdJDpXjMi7Brhkr1mXsXrUJvjqM2mQDgkRiz8jFaQGOdaLxgjtUfQgZhKy/O3cG/YwmgKaxLA==} engines: {node: '>=12.22.0'} '@pnpm/npm-conf@3.0.3': resolution: {integrity: sha512-//0sR/cow/s4ICQaYoAobOl4aU8cjU6x/V24V7XkKotb9+O+3zySIYp146vpaobYHnxa4pZX8NkV54Z5AwbDKA==} engines: {node: '>=12'} '@polka/url@1.0.0-next.29': resolution: {integrity: sha512-wwQAWhWSuHaag8c4q/KN/vCoeOJYshAIvMQwD4GpSb3OiZklFfvAgmj0VCBBImRpuF/aFgIRzllXlVX93Jevww==} '@sec-ant/readable-stream@0.4.1': resolution: {integrity: sha512-831qok9r2t8AlxLko40y2ebgSDhenenCatLVeW/uBtnHPyhHOvG0C7TvfgecV+wHzIm5KUICgzmVpWS+IMEAeg==} '@semantic-release/changelog@6.0.3': resolution: {integrity: sha512-dZuR5qByyfe3Y03TpmCvAxCyTnp7r5XwtHRf/8vD9EAn4ZWbavUX8adMtXYzE86EVh0gyLA7lm5yW4IV30XUag==} engines: {node: '>=14.17'} peerDependencies: semantic-release: '>=18.0.0' '@semantic-release/commit-analyzer@13.0.1': resolution: {integrity: sha512-wdnBPHKkr9HhNhXOhZD5a2LNl91+hs8CC2vsAVYxtZH3y0dV3wKn+uZSN61rdJQZ8EGxzWB3inWocBHV9+u/CQ==} engines: {node: '>=20.8.1'} peerDependencies: semantic-release: '>=20.1.0' '@semantic-release/error@3.0.0': resolution: {integrity: sha512-5hiM4Un+tpl4cKw3lV4UgzJj+SmfNIDCLLw0TepzQxz9ZGV5ixnqkzIVF+3tp0ZHgcMKE+VNGHJjEeyFG2dcSw==} engines: {node: '>=14.17'} '@semantic-release/error@4.0.0': resolution: {integrity: sha512-mgdxrHTLOjOddRVYIYDo0fR3/v61GNN1YGkfbrjuIKg/uMgCd+Qzo3UAXJ+woLQQpos4pl5Esuw5A7AoNlzjUQ==} engines: {node: '>=18'} '@semantic-release/git@10.0.1': resolution: {integrity: sha512-eWrx5KguUcU2wUPaO6sfvZI0wPafUKAMNC18aXY4EnNcrZL86dEmpNVnC9uMpGZkmZJ9EfCVJBQx4pV4EMGT1w==} engines: {node: '>=14.17'} peerDependencies: semantic-release: '>=18.0.0' '@semantic-release/github@12.0.9': resolution: {integrity: sha512-ODIqb0V3QqndipryEEiaBxUQCFjvv7Oese5Dt4omMGa60YRNEW0Sx3K+zri0uac2Y6S9nOlMehciWIzvvRCTGQ==} engines: {node: ^22.14.0 || >= 24.10.0} peerDependencies: semantic-release: '>=24.1.0' '@semantic-release/npm@13.1.5': resolution: {integrity: sha512-Hq5UxzoatN3LHiq2rTsWS54nCdqJHlsssGERCo8WlvdfFA9LoN0vO+OuKVSjtNapIc/S8C2LBj206wKLHg62mg==} engines: {node: ^22.14.0 || >= 24.10.0} peerDependencies: semantic-release: '>=20.1.0' '@semantic-release/release-notes-generator@14.1.1': resolution: {integrity: sha512-Pbd2e2XRMUD0OxehHpgd5/YghsE76cddkRHSoDvKLK+OCy4Ewxn49rWR631MEUU01lgwF/uyVXvbnVuu6+Z6VA==} engines: {node: '>=20.8.1'} peerDependencies: semantic-release: '>=20.1.0' '@simple-libs/stream-utils@1.2.0': resolution: {integrity: sha512-KxXvfapcixpz6rVEB6HPjOUZT22yN6v0vI0urQSk1L8MlEWPDFCZkhw2xmkyoTGYeFw7tWTZd7e3lVzRZRN/EA==} engines: {node: '>=18'} '@sindresorhus/base62@1.0.0': resolution: {integrity: sha512-TeheYy0ILzBEI/CO55CP6zJCSdSWeRtGnHy8U8dWSUH4I68iqTsy7HkMktR4xakThc9jotkPQUXT4ITdbV7cHA==} engines: {node: '>=18'} '@sindresorhus/is@4.6.0': resolution: {integrity: sha512-t09vSN3MdfsyCHoFcTRCH/iUtG7OJ0CsjzB8cjAmKc/va/kIgeDI/TxsigdncE/4be734m0cvIYwNaV4i2XqAw==} engines: {node: '>=10'} '@sindresorhus/merge-streams@4.0.0': resolution: {integrity: sha512-tlqY9xq5ukxTUZBmoOp+m61cqwQD5pHJtFY3Mn8CA8ps6yghLH/Hw8UPdqg4OLmFW3IFlcXnQNmo/dh8HzXYIQ==} engines: {node: '>=18'} '@standard-schema/spec@1.1.0': resolution: {integrity: sha512-l2aFy5jALhniG5HgqrD6jXLi/rUWrKvqN/qJx6yoJsgKhblVd+iqqU4RCXavm/jPityDo5TCvKMnpjKnOriy0w==} '@stylistic/eslint-plugin@5.10.0': resolution: {integrity: sha512-nPK52ZHvot8Ju/0A4ucSX1dcPV2/1clx0kLcH5wDmrE4naKso7TUC/voUyU1O9OTKTrR6MYip6LP0ogEMQ9jPQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^9.0.0 || ^10.0.0 '@testing-library/dom@10.4.1': resolution: {integrity: sha512-o4PXJQidqJl82ckFaXUeoAW+XysPLauYI43Abki5hABd853iMhitooc6znOnczgbTYmEP6U6/y1ZyKAIsvMKGg==} engines: {node: '>=18'} '@testing-library/user-event@14.6.1': resolution: {integrity: sha512-vq7fv0rnt+QTXgPxr5Hjc210p6YKq2kmdziLgnsZGgLJ9e6VAShx1pACLuRjd/AS/sr7phAR58OIIpf0LlmQNw==} engines: {node: '>=12', npm: '>=6'} peerDependencies: '@testing-library/dom': '>=7.21.4' '@tybys/wasm-util@0.10.3': resolution: {integrity: sha512-F3fo1MYrRJYL3zER0OUOmkutjr1Vp23m7OsSgp7nq4SP6OqX6C/56XFIPAl5bt3zaBRjmW7SGz3u/6LwFpYcOg==} '@types/aria-query@5.0.4': resolution: {integrity: sha512-rfT93uj5s0PRL7EzccGMs3brplhcrghnDoV26NqKhCAS1hVo+WdNsPvE/yb6ilfr5hi2MEk6d5EWJTKdxg8jVw==} '@types/chai@5.2.3': resolution: {integrity: sha512-Mw558oeA9fFbv65/y4mHtXDs9bPnFMZAL/jxdPFUpOHHIXX91mcgEHbS5Lahr+pwZFR8A7GQleRWeI6cGFC2UA==} '@types/debug@4.1.13': resolution: {integrity: sha512-KSVgmQmzMwPlmtljOomayoR89W4FynCAi3E8PPs7vmDVPe84hT+vGPKkJfThkmXs0x0jAaa9U8uW8bbfyS2fWw==} '@types/deep-eql@4.0.2': resolution: {integrity: sha512-c9h9dVVMigMPc4bwTvC5dxqtqJZwQPePsWjPlpSOnojbor6pGqdk541lfA7AqFQr5pB1BRdq0juY9db81BwyFw==} '@types/esrecurse@4.3.1': resolution: {integrity: sha512-xJBAbDifo5hpffDBuHl0Y8ywswbiAp/Wi7Y/GtAgSlZyIABppyurxVueOPE8LUQOxdlgi6Zqce7uoEpqNTeiUw==} '@types/estree@1.0.9': resolution: {integrity: sha512-GhdPgy1el4/ImP05X05Uw4cw2/M93BCUmnEvWZNStlCzEKME4Fkk+YpoA5OiHNQmoS7Cafb8Xa3Pya8m1Qrzeg==} '@types/json-schema@7.0.15': resolution: {integrity: sha512-5+fP8P8MFNC+AyZCDxrB2pkZFPGzqQWUzpSeuuVLvm8VMcorNYavBqoFcxK8bQz4Qsbn4oUEEem4wDLfcysGHA==} '@types/katex@0.16.8': resolution: {integrity: sha512-trgaNyfU+Xh2Tc+ABIb44a5AYUpicB3uwirOioeOkNPPbmgRNtcWyDeeFRzjPZENO9Vq8gvVqfhaaXWLlevVwg==} '@types/ms@2.1.0': resolution: {integrity: sha512-GsCCIZDE/p3i96vtEqx+7dBUGXrc7zeSK3wwPHIaRThS+9OhWIXRqzs4d6k1SVU8g91DrNRWxWUGhp5KXQb2VA==} '@types/node@26.1.1': resolution: {integrity: sha512-nxAkRSVkN1Y0JC1W8ky/fTfkGsMmcrRsbx+3XoZE+rMOX71kLYTV7fLXpqud1GpbpP5TuffXFqfX7fH2GgZREw==} '@types/normalize-package-data@2.4.4': resolution: {integrity: sha512-37i+OaWTh9qeK4LSHPsyRC7NahnGotNuZvjLSgcPzblpHB3rrCJxAOgI5gCdKm7coonsaX1Of0ILiTcnZjbfxA==} '@types/unist@2.0.11': resolution: {integrity: sha512-CmBKiL6NNo/OqgmMn95Fk9Whlp2mtvIv+KNpQKN2F4SjvrEesubTRWGYSg+BnWZOnlCaSTU1sMpsBOzgbYhnsA==} '@typescript-eslint/eslint-plugin@8.58.2': resolution: {integrity: sha512-aC2qc5thQahutKjP+cl8cgN9DWe3ZUqVko30CMSZHnFEHyhOYoZSzkGtAI2mcwZ38xeImDucI4dnqsHiOYuuCw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/parser': ^8.58.2 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/parser@8.58.2': resolution: {integrity: sha512-/Zb/xaIDfxeJnvishjGdcR4jmr7S+bda8PKNhRGdljDM+elXhlvN0FyPSsMnLmJUrVG9aPO6dof80wjMawsASg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/project-service@8.58.2': resolution: {integrity: sha512-Cq6UfpZZk15+r87BkIh5rDpi38W4b+Sjnb8wQCPPDDweS/LRCFjCyViEbzHk5Ck3f2QDfgmlxqSa7S7clDtlfg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/scope-manager@8.58.2': resolution: {integrity: sha512-SgmyvDPexWETQek+qzZnrG6844IaO02UVyOLhI4wpo82dpZJY9+6YZCKAMFzXb7qhx37mFK1QcPQ18tud+vo6Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/tsconfig-utils@8.58.2': resolution: {integrity: sha512-3SR+RukipDvkkKp/d0jP0dyzuls3DbGmwDpVEc5wqk5f38KFThakqAAO0XMirWAE+kT00oTauTbzMFGPoAzB0A==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/type-utils@8.58.2': resolution: {integrity: sha512-Z7EloNR/B389FvabdGeTo2XMs4W9TjtPiO9DAsmT0yom0bwlPyRjkJ1uCdW1DvrrrYP50AJZ9Xc3sByZA9+dcg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/types@8.58.2': resolution: {integrity: sha512-9TukXyATBQf/Jq9AMQXfvurk+G5R2MwfqQGDR2GzGz28HvY/lXNKGhkY+6IOubwcquikWk5cjlgPvD2uAA7htQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/types@8.63.0': resolution: {integrity: sha512-xyLtl9DUBBFrcJS4x2pIqGLH68/tC2uOa4Z7pUteW09D3bXnnXUom4dyPikzWgB7llmIc1zoeI3aoUdC4rPK/Q==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript-eslint/typescript-estree@8.58.2': resolution: {integrity: sha512-ELGuoofuhhoCvNbQjFFiobFcGgcDCEm0ThWdmO4Z0UzLqPXS3KFvnEZ+SHewwOYHjM09tkzOWXNTv9u6Gqtyuw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/utils@8.58.2': resolution: {integrity: sha512-QZfjHNEzPY8+l0+fIXMvuQ2sJlplB4zgDZvA+NmvZsZv3EQwOcc1DuIU1VJUTWZ/RKouBMhDyNaBMx4sWvrzRA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' '@typescript-eslint/visitor-keys@8.58.2': resolution: {integrity: sha512-f1WO2Lx8a9t8DARmcWAUPJbu0G20bJlj8L4z72K00TMeJAoyLr/tHhI/pzYBLrR4dXWkcxO1cWYZEOX8DKHTqA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} '@typescript/typescript-aix-ppc64@7.0.2': resolution: {integrity: sha512-MTKKkWB7p/0E9xi1d1tHtZ5PiLkGEMIq88pK2CubZjOsLtYTLqhgIgi6zepFa+9GHZ6h05NMCkQxGKiPXMxXtQ==} engines: {node: '>=16.20.0'} cpu: [ppc64] os: [aix] '@typescript/typescript-darwin-arm64@7.0.2': resolution: {integrity: sha512-gowzar9MwS/aRWp6f3a4KUqzRjAZjOsmGNCM6LcTgXum+dBfgsBVMN+AgvOCCbguXyick6LJhpBszxMebJ8syA==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [darwin] '@typescript/typescript-darwin-x64@7.0.2': resolution: {integrity: sha512-SZ9xZInqApNlNGc9s0W1VSsktYSOe9cFqNOIqmN1Gs8SmkjKZYFt017G4VwPxASInODuAdbTW7sXiFUf893RgA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [darwin] '@typescript/typescript-freebsd-arm64@7.0.2': resolution: {integrity: sha512-W5NH4y/J0plIIS5b2xvTEkU7JFxyqdMAOgf+Ilhl0vHQXKO5dZoxd+C/jEtq56c4F3wk71RB4BMRQ2XdI+bwYQ==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [freebsd] '@typescript/typescript-freebsd-x64@7.0.2': resolution: {integrity: sha512-UMGDx5sTpzNw3WiPebH7l90IWfJggEd+egHt/q6p7/Cm3zqoV7VxkGXt+3DxPIw8CcmvAB0j3sVVfbhX+M4Tpw==} engines: {node: '>=16.20.0'} cpu: [x64] os: [freebsd] '@typescript/typescript-linux-arm64@7.0.2': resolution: {integrity: sha512-Qh4eU4/y3yDjnfjjyPYihMj5/ODIlmt+Bzu17OI+fiSRDW57QmU5SiN63exPRNJPKUzcc1INa1NXdrJ+MqHjUQ==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [linux] '@typescript/typescript-linux-arm@7.0.2': resolution: {integrity: sha512-gffT3xPz9sR7j/YJExkyPntrI0P2EP9XbOyWzth2/Gs0RstK+90RBcO0ncXoXy/beYll1SXw846Nf2zdnEz0QQ==} engines: {node: '>=16.20.0'} cpu: [arm] os: [linux] '@typescript/typescript-linux-loong64@7.0.2': resolution: {integrity: sha512-uEHck9i8hoAzXPiYRib1O7miOnz23SxIeVl6F4LXox+qov1K35jHcEW6VHKvZI+pyvl7fZEP4MCU5LYvIq1GuQ==} engines: {node: '>=16.20.0'} cpu: [loong64] os: [linux] '@typescript/typescript-linux-mips64el@7.0.2': resolution: {integrity: sha512-R4KvAMnE43W5Qeqb0Ly56O3mWMWIAgsMyz36DCaycd5nbg/9kzm0liw3JocfRqyJY0KPmzFjbswozXyW0DnIYA==} engines: {node: '>=16.20.0'} cpu: [mips64el] os: [linux] '@typescript/typescript-linux-ppc64@7.0.2': resolution: {integrity: sha512-DORx5b3sd/4S7eayxm4FQv+A7CrkUIGRaHiwI8oiHTAI1fAPWhF4J0vAlkC8biAlHSVVwxMQ3tjZ2/DVbnQiiA==} engines: {node: '>=16.20.0'} cpu: [ppc64] os: [linux] '@typescript/typescript-linux-riscv64@7.0.2': resolution: {integrity: sha512-wf0jqEDOjrPRnKwYRyyJDRo11KMbvMFrU+q4zqKyChODBzvlkbhNQfKvLxQCcwTpdDaXSHZTVuh0JoCrKCUMHQ==} engines: {node: '>=16.20.0'} cpu: [riscv64] os: [linux] '@typescript/typescript-linux-s390x@7.0.2': resolution: {integrity: sha512-IkwJc3L7yhytWd/ewjyxNDfOmswCm9GWMJT/ue/dU4aZNbwZeYAetq42VyLmsmSjvoX7z74X6ZaYCtzAr0EuGw==} engines: {node: '>=16.20.0'} cpu: [s390x] os: [linux] '@typescript/typescript-linux-x64@7.0.2': resolution: {integrity: sha512-EYdf2cNg7rgCWJnxCdJ+F3V39O8ihb37eHAu1LK8oAFizgTQbPOK7zHHXbPt8rX24COqODXeI3sIf0fCXG7H/A==} engines: {node: '>=16.20.0'} cpu: [x64] os: [linux] '@typescript/typescript-netbsd-arm64@7.0.2': resolution: {integrity: sha512-+polYF4MF04aPpO5FTkHran9yUQDSXqy5GiSDKpsll5jy3l3+g9QLhpf39T+ePtefhXLOGrLl0QIjkQP6VnelA==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [netbsd] '@typescript/typescript-netbsd-x64@7.0.2': resolution: {integrity: sha512-8YIT0EHM/3dq10ZOVF/A7pc/YSMtbcecct4rWtexrnSCHOPcpC2KTLXfTCR6vDpnSiY12heNb1GiN/wu+T/FyA==} engines: {node: '>=16.20.0'} cpu: [x64] os: [netbsd] '@typescript/typescript-openbsd-arm64@7.0.2': resolution: {integrity: sha512-APT8+ClYnuYm1u9+kgGXoMj2VzWzcymwh2gNSQVySHfkRDGOTVkoWLjCmOQSaO+PoqQ57B0flRp9SA+7GnnkzQ==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [openbsd] '@typescript/typescript-openbsd-x64@7.0.2': resolution: {integrity: sha512-yX7s+Q0Dln0Dt9tEzZsAjXXR/+ytBM7AlglaqyeMPxQszJ1JhlJdZ6jLA+IzldHtflX81em7lDao1xXu+aRRkg==} engines: {node: '>=16.20.0'} cpu: [x64] os: [openbsd] '@typescript/typescript-sunos-x64@7.0.2': resolution: {integrity: sha512-dLJDGaLZ1D4HPQn62u1n8mBDkJREwMsAkCdkwd4Ieqw+x3TUyTsqY0YiBCtE6H6OzzgGk3iuZ3vFWRS+E8/d1g==} engines: {node: '>=16.20.0'} cpu: [x64] os: [sunos] '@typescript/typescript-win32-arm64@7.0.2': resolution: {integrity: sha512-Gyl1Vy6OsWesLzmq+EP0Fb7b4Nid5232AvcA2SFcdYreldpNtYFFofPjnt62y9hQy7VTaZp65ICJjuAQRaVcIQ==} engines: {node: '>=16.20.0'} cpu: [arm64] os: [win32] '@typescript/typescript-win32-x64@7.0.2': resolution: {integrity: sha512-0BQ3HkAHHlKLSp1qRvf3SUhGpGsDuhB/jgFw75guyqbxJqEaS0Cw/VFO8i2nHglJUzQCRtMMR/IBAKE3ETMC4g==} engines: {node: '>=16.20.0'} cpu: [x64] os: [win32] '@unrs/resolver-binding-android-arm-eabi@1.12.2': resolution: {integrity: sha512-g5T90pqg1bo/7mytQx6F4iBNC0Wsh9cu+z9veDbFjc7HjpesJFWD7QMS0NGStXM075+7dJPPVvBbpZlnrdpi/w==} cpu: [arm] os: [android] '@unrs/resolver-binding-android-arm64@1.12.2': resolution: {integrity: sha512-YGCRZv/9GLhwmz6mYDeTsm/92BAyR28l6c2ReweVW5pWgfsitWLY8upvfRlGdoyD8HjeTHSYJWyZGD4KJA/nFQ==} cpu: [arm64] os: [android] '@unrs/resolver-binding-darwin-arm64@1.12.2': resolution: {integrity: sha512-u9DiNT1auQMO20A9SyTuG3wUgQWB9Z7KjAg0uFuCDR1FsAY8A0CG2S6JpHS1xwm/w1G08bjXZDcyOCjv1WAm2w==} cpu: [arm64] os: [darwin] '@unrs/resolver-binding-darwin-x64@1.12.2': resolution: {integrity: sha512-f7rPLi/T1HVKZu/u6t87lroib16n8vrSzcyxI7lg4BGO9UF26KhQL44sd9eOUgrTYhvRXtWOIZT5PejdPyJfUA==} cpu: [x64] os: [darwin] '@unrs/resolver-binding-freebsd-x64@1.12.2': resolution: {integrity: sha512-BpcOjWCJub6nRZUS2zA20pmLvjtqAtGejETaIyRLiZiQf++cbrjltLA5NN/xaXfqeOBOSlMFbemIl5/S5tljmg==} cpu: [x64] os: [freebsd] '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': resolution: {integrity: sha512-vZTDvdSISZjJx66OzJqtsOhzifbqRjbmI1Mnu49fQDwog5GtDI4QidRiEAYbZCRj9C8YZEW+3ZjqsyS9GR4k2A==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': resolution: {integrity: sha512-BiPI+IrIlwcW4nLLMM21+B1dFPzd55yAVgVGrdgDjNef+ch03GdxrcyaIz8X9SsQirh/kCQ7mviyWlMxdh2D7g==} cpu: [arm] os: [linux] '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': resolution: {integrity: sha512-zJc0H99FEPoFfSrNpa91HYfxzfAJCr502oxNK1cfdC9hlaFI43RT+JFCann9JUgZmLzzntChHyn13Sgn9ljHNg==} cpu: [arm64] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-arm64-musl@1.12.2': resolution: {integrity: sha512-KQ3Lki6l+Pz1k/eBipN41ES+YUK30beLGb9YqcB1O542cyLCNE6GaxrfcY3T6EezmGGk84wb5XyO9loTM9tkcA==} cpu: [arm64] os: [linux] libc: [musl] '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': resolution: {integrity: sha512-3SJGEh1DborhG6pyxvhPzCT4bbSIVihsvgJc13P1bHG7KLdNDaF9T3gsTwFc7Jw/5Y5/iWOjkEx7Zy0NvCGX3Q==} cpu: [loong64] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-loong64-musl@1.12.2': resolution: {integrity: sha512-jiuG/Obbel7uw1PwHNFfrkiKhLAF6mnyZ6aWlOAVN9WqKm8v0OFGnciJIHu8+CMvXLQ8AD51LPzAoUfT21D5Ew==} cpu: [loong64] os: [linux] libc: [musl] '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': resolution: {integrity: sha512-q7xRvVpmcfeL+LlZg8Pbbo6QaTZwDU5BaGZbwfhkEsXJn3Was8xYfE0RBH266xZt0rM6B7i8xAYIvjthuUIWHg==} cpu: [ppc64] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': resolution: {integrity: sha512-0CVdx6lcnT3Q9inOH8tsMIOJ6ImndllMjqJHg8RLVdB7Vq4SfkEXl9mCSsVNuNA4MCYycRicCUxPCabVHJRr6A==} cpu: [riscv64] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': resolution: {integrity: sha512-iOwlRo9vnp6R6ohHQS11n0NnfdXx/omhkocmIfaPRpQhKZ+3BDMkkdRVh53qjkFkpPddf+FETA28NwGN7l5l+w==} cpu: [riscv64] os: [linux] libc: [musl] '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': resolution: {integrity: sha512-HYJtLfXq94q8iZNFT1lknx258wlkkWhZeUXJRqzKBBUJ00CvZ+N33zgbCqimLjsyw5Va6uUxhVa12mI+kaveEw==} cpu: [s390x] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-x64-gnu@1.12.2': resolution: {integrity: sha512-mPsUhunKKDih5O96Y6enDQyHc1SqBPlY1E/SfMWDM3EdJ95Z9CArPeCVwCCqbP45ljvivdEk8Fxn+SIb1rDAJQ==} cpu: [x64] os: [linux] libc: [glibc] '@unrs/resolver-binding-linux-x64-musl@1.12.2': resolution: {integrity: sha512-azrt6+5ydLd8Vt210AAFis/lZevSfPw93EJRIJG+xPu4WCJ8K0kppCTpMyLPcKT7H15M4Jnt2tMp5bOvCkRC6A==} cpu: [x64] os: [linux] libc: [musl] '@unrs/resolver-binding-openharmony-arm64@1.12.2': resolution: {integrity: sha512-YZ9hP4O0X9PQb8eO980qmLNGH4zT3I9+SZTdt0Pr0YyuGQhYKoOZkV02VzrzyOZJ5xIJ3UFIenKkUkGg8GjgWQ==} cpu: [arm64] os: [openharmony] '@unrs/resolver-binding-wasm32-wasi@1.12.2': resolution: {integrity: sha512-tYFDIkMxSflfEc/h92ZWNsZlHSwgimbNHSO3PL2JWQHfCuC2q316jMyYU9TIWZsFK2bQwyK5VAdYgn8ygPj69A==} engines: {node: '>=14.0.0'} cpu: [wasm32] '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': resolution: {integrity: sha512-qzNyg3xL0VPQmCaUh+N5jSitce6k+uCBfMDesWRnlULOZaqUkaJ0ybdT+UqlAWJoQjuqfIU/0Ptx9bteN4D82g==} cpu: [arm64] os: [win32] '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': resolution: {integrity: sha512-WD9sY00OfpHVGfsnHZoA8jVT+esS/Bg8z8jzxp5BnDCjjwsuKsPQrzswwpFy4J1AUJbXPRfkpcX0mXrzeXW79g==} cpu: [ia32] os: [win32] '@unrs/resolver-binding-win32-x64-msvc@1.12.2': resolution: {integrity: sha512-nAB74NfSNKknqQ1RrYj6uz8FcXEomu/MATJZxh/x+BArzN2U3JbOYC0APYzUIGhVY3m5hRxA8VPNdPBoG8txlA==} cpu: [x64] os: [win32] '@vitest/browser-preview@4.1.10': resolution: {integrity: sha512-14MJrL59ZFkqXLjwfSk6RzTDy5Czf9UG4+8q8L6Gxjs2aPjEce/cVNYV14bXAc2BvMjUNu904+ZEZA1Xc1wtvQ==} peerDependencies: vitest: 4.1.10 '@vitest/browser@4.1.10': resolution: {integrity: sha512-UDwuWGwXj646CBx/bQHOaJSX7np0I8JL/UKQYa1e4QrVHH8VdWtx8eaOuf8sy0ShwDgR6NjJAsp5eF6vjF6qng==} peerDependencies: vitest: 4.1.10 '@vitest/expect@4.1.10': resolution: {integrity: sha512-YsCn+qAk1GWjQOWFEsEcL2gNQ0zmVmQu3T03qP6UyjhtmdtwtbuI+DASn/7iQB3HGTXkdBwGddzxPlmiql5vlA==} '@vitest/mocker@4.1.10': resolution: {integrity: sha512-v0xaezt+DKEmKfaxg133ldzADrwLGd7Ze1MfQQTYfvs8OqZIwbxyxaYURivwV7sWy5fqn3rH5uOrSp07bp44Ow==} peerDependencies: msw: ^2.4.9 vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: msw: optional: true vite: optional: true '@vitest/pretty-format@4.1.10': resolution: {integrity: sha512-W1HsjSH4MXQ9YfmmhLAoIYf1HRfekQCGngeIgcei6MP5QQGWUe0gkopdZQaVCFO+JDJMrAJGwa5pRpNpvy4P8Q==} '@vitest/runner@4.1.10': resolution: {integrity: sha512-IKI6kpIH+LmpROplyLwBBaCfMgOZOMsygVa6BARD6ahA04VRuJSa6OaVG7kRvSEMD870Vd91rSSw0eegtWyLGg==} '@vitest/snapshot@4.1.10': resolution: {integrity: sha512-xRkfOT1qpTAi/Ti4Y1LtfRc3kEuqxGw59eN2jN9pRWMtS/XDevekhcFSqvQqjUNGksfjMJu3Y+oJ+4Ypn2OaJw==} '@vitest/spy@4.1.10': resolution: {integrity: sha512-PLf/Ugvoq5wO/b4rwYCR1h2PSIdXz7wnkQFMiUpLdtM7l6pqVFcQIBEHyT1+l+cj7mNwAfZHzqXqDyjvOuwbDw==} '@vitest/utils@4.1.10': resolution: {integrity: sha512-fy9am/HWxbaGt/Sawrp90vt6Y6jQwf1RX77cz3uwoJwJVMli/e1IEwRPnMNJ7vKfPTwo0diXifkpPvwH9v7nGA==} '@voidzero-dev/vite-plus-core@0.2.4': resolution: {integrity: sha512-AoAYGPwNO56o9TuCR+KaQGA5XpSnTpn2QYHK0DQ0f8j3wvaMmToeWOJF6STo+XMntMDfiaB7sOsSFNE+hPYyjg==} engines: {node: ^20.19.0 || ^22.18.0 || >=24.11.0} peerDependencies: '@arethetypeswrong/core': ^0.18.1 '@types/node': ^20.19.0 || >=22.12.0 '@vitejs/devtools': ^0.3.0 esbuild: ^0.27.0 || ^0.28.0 jiti: '>=1.21.0' less: ^4.0.0 publint: ^0.3.8 sass: ^1.70.0 sass-embedded: ^1.70.0 stylus: '>=0.54.8' sugarss: ^5.0.0 terser: ^5.16.0 tsx: ^4.8.1 typescript: ^5.0.0 || ^6.0.0 unplugin-unused: ^0.5.0 unrun: '*' yaml: ^2.4.2 peerDependenciesMeta: '@arethetypeswrong/core': optional: true '@types/node': optional: true '@vitejs/devtools': optional: true esbuild: optional: true jiti: optional: true less: optional: true publint: optional: true sass: optional: true sass-embedded: optional: true stylus: optional: true sugarss: optional: true terser: optional: true tsx: optional: true typescript: optional: true unplugin-unused: optional: true unrun: optional: true yaml: optional: true '@voidzero-dev/vite-plus-darwin-arm64@0.2.4': resolution: {integrity: sha512-UQwoLpnBW3qLqj5H3airPQeMCX3kfffVDM2EYGe889Fn5dOS9VhikuWloJlcjwtKwqLbFqkrsGjfb6XRvuLozg==} engines: {node: '>=20.0.0'} cpu: [arm64] os: [darwin] '@voidzero-dev/vite-plus-darwin-x64@0.2.4': resolution: {integrity: sha512-f4XtiA4cBc/Z260QvvXIlBqTb/dZMAioohQDoR5jLG9o9vIOaWE2Ujm/zcWDyNpyZ88RLRrcO8ZwiMrEsdzbJw==} engines: {node: '>=20.0.0'} cpu: [x64] os: [darwin] '@voidzero-dev/vite-plus-linux-arm64-gnu@0.2.4': resolution: {integrity: sha512-TUamG9wEehZI4SFG7M6nUKb6z/7x3nNOBbnPdWIpv4C8uWKHwNtsnaaVLeygHIUIJEhbByR3oEFDylYLLr4KRw==} engines: {node: '>=20.0.0'} cpu: [arm64] os: [linux] libc: [glibc] '@voidzero-dev/vite-plus-linux-arm64-musl@0.2.4': resolution: {integrity: sha512-CS9uQ22QFr+lZZp95Huccg3cip3QYVU9GWrhvATqMBXWXb8IRHOulzuXrFxzvhMBITyPaRS9m/eIHmnM4L4h4Q==} engines: {node: '>=20.0.0'} cpu: [arm64] os: [linux] libc: [musl] '@voidzero-dev/vite-plus-linux-x64-gnu@0.2.4': resolution: {integrity: sha512-X5XfvftFERjer78SG+QKaJCa9LgIKygx4w13sD1/RS/kYOtaf1+yifrJpOFel+t9TRe/YqGTZa5C8uFrDz3OHw==} engines: {node: '>=20.0.0'} cpu: [x64] os: [linux] libc: [glibc] '@voidzero-dev/vite-plus-linux-x64-musl@0.2.4': resolution: {integrity: sha512-2iIWW6JR0UOK4QIFKgUQHjaF/7hZxELePny8R1OIn2jzShRfQhS1cmxksSg8ce/5nhYrfQ9dkg5ZmdLbxhpS/A==} engines: {node: '>=20.0.0'} cpu: [x64] os: [linux] libc: [musl] '@voidzero-dev/vite-plus-win32-arm64-msvc@0.2.4': resolution: {integrity: sha512-3yY4FnpvKBxjmHtEcao6Wcs/VBstzC0GhXAPWetbiFEl/sgL66V4Uhws02esfOSWw8abqLrXyPE9vK+newtsuw==} engines: {node: '>=20.0.0'} cpu: [arm64] os: [win32] '@voidzero-dev/vite-plus-win32-x64-msvc@0.2.4': resolution: {integrity: sha512-ERnFrh1O0XZhmGSqND04jtHM7tyKaGAOeT1w/HpVFmL/cQK2vuLl2GKjEwOqkBLd2csNGCcnk3e7C2qDmrNR/Q==} engines: {node: '>=20.0.0'} cpu: [x64] os: [win32] acorn-jsx@5.3.2: resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 acorn@8.17.0: resolution: {integrity: sha512-xRQbDb9BnwDafYNn6Vwl839DYVjqXYb1XVGtWAZ1kcDc6iwAL4hg3B1dZlRiuENFeO2H53gFG3in621AdERVAg==} engines: {node: '>=0.4.0'} hasBin: true agent-base@9.0.0: resolution: {integrity: sha512-TQf59BsZnytt8GdJKLPfUZ54g/iaUL2OWDSFCCvMOhsHduDQxO8xC4PNeyIkVcA5KwL2phPSv0douC0fgWzmnA==} engines: {node: '>= 20'} aggregate-error@3.1.0: resolution: {integrity: sha512-4I7Td01quW/RpocfNayFdFVk1qSuoh0E7JrbRJ16nH01HhKFQ88INq9Sd+nd72zqRySlr9BmDA8xlEJ6vJMrYA==} engines: {node: '>=8'} aggregate-error@5.0.0: resolution: {integrity: sha512-gOsf2YwSlleG6IjRYG2A7k0HmBMEo6qVNk9Bp/EaLgAJT5ngH6PXbqa4ItvnEwCm/velL5jAnQgsHsWnjhGmvw==} engines: {node: '>=18'} ajv@6.15.0: resolution: {integrity: sha512-fgFx7Hfoq60ytK2c7DhnF8jIvzYgOMxfugjLOSMHjLIPgenqa7S7oaagATUq99mV6IYvN2tRmC0wnTYX6iPbMw==} ansi-escapes@7.3.0: resolution: {integrity: sha512-BvU8nYgGQBxcmMuEeUEmNTvrMVjJNSH7RgW24vXexN4Ven6qCvy4TntnvlnwnMLTVlcRQQdbRY8NKnaIoeWDNg==} engines: {node: '>=18'} ansi-regex@5.0.1: resolution: {integrity: sha512-quJQXlTSUGL2LH9SUXo8VwsY4soanhgo6LNSm84E1LBcE8s3O0wpdiRzyR9z/ZZJMlMWv37qOOb9pdJlMUEKFQ==} engines: {node: '>=8'} ansi-regex@6.2.2: resolution: {integrity: sha512-Bq3SmSpyFHaWjPk8If9yc6svM8c56dB5BAtW4Qbw5jHTwwXXcTLoRMkpDJp6VL0XzlWaCHTXrkFURMYmD0sLqg==} engines: {node: '>=12'} ansi-styles@3.2.1: resolution: {integrity: sha512-VT0ZI6kZRdTh8YyJw3SMbYm/u+NqfsAxEpWO0Pf9sq8/e94WxxOpPKx9FR1FlyCtOVDNOQ+8ntlqFxiRc+r5qA==} engines: {node: '>=4'} ansi-styles@4.3.0: resolution: {integrity: sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==} engines: {node: '>=8'} ansi-styles@5.2.0: resolution: {integrity: sha512-Cxwpt2SfTzTtXcfOlzGEee8O+c+MmUgGrNiBcXnuWxuFJHe6a5Hz7qwhwe5OgaSYI0IJvkLqWX1ASG+cJOkEiA==} engines: {node: '>=10'} ansi-styles@6.2.3: resolution: {integrity: sha512-4Dj6M28JB+oAH8kFkTLUo+a2jwOFkuqb3yucU0CANcRRUbxS0cP0nZYCGjcc3BNXwRIsUVmDGgzawme7zvJHvg==} engines: {node: '>=12'} any-promise@1.3.0: resolution: {integrity: sha512-7UvmKalWRt1wgjL1RrGxoSJW/0QZFIegpeGvZG9kjp8vrRu55XTHbwnqq2GpXm9uLbcuhxm3IqX9OB4MZR1b2A==} are-docs-informative@0.0.2: resolution: {integrity: sha512-ixiS0nLNNG5jNQzgZJNoUpBKdo9yTYZMGJ+QgT2jmjR7G7+QHRCc4v6LQ3NgE7EBJq+o0ams3waJwkrlBom8Ig==} engines: {node: '>=14'} argparse@2.0.1: resolution: {integrity: sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==} argv-formatter@1.0.0: resolution: {integrity: sha512-F2+Hkm9xFaRg+GkaNnbwXNDV5O6pnCFEmqyhvfC/Ic5LbgOWjJh3L+mN/s91rxVL3znE7DYVpW0GJFT+4YBgWw==} aria-query@5.3.0: resolution: {integrity: sha512-b0P0sZPKtyu8HkeRAfCq0IfURZK+SuwMjY1UXGBU27wpAiTwQAIlq56IbIO+ytk/JjS1fMR14ee5WBBfKi5J6A==} aria-query@5.3.2: resolution: {integrity: sha512-COROpnaoap1E2F000S62r6A60uHZnmlvomhfyT2DlTcrY1OrBKn2UhH7qn5wTC9zMvD0AY7csdPSNwKP+7WiQw==} engines: {node: '>= 0.4'} array-buffer-byte-length@1.0.2: resolution: {integrity: sha512-LHE+8BuR7RYGDKvnrmcuSq3tDcKv9OFEXQt/HpbZhY7V6h0zlUXutnAD82GiFx9rdieCMjkvtcsPqBwgUl1Iiw==} engines: {node: '>= 0.4'} array-ify@1.0.0: resolution: {integrity: sha512-c5AMf34bKdvPhQ7tBGhqkgKNUzMr4WUs+WDtC2ZUGOUncbxKMTvqxYctiseW3+L4bA8ec+GcZ6/A/FW4m8ukng==} array-includes@3.1.9: resolution: {integrity: sha512-FmeCCAenzH0KH381SPT5FZmiA/TmpndpcaShhfgEN9eCVjnFBqq3l1xrI42y8+PPLI6hypzou4GXw00WHmPBLQ==} engines: {node: '>= 0.4'} array.prototype.flat@1.3.3: resolution: {integrity: sha512-rwG/ja1neyLqCuGZ5YYrznA62D4mZXg0i1cIskIUKSiqF3Cje9/wXAls9B9s1Wa2fomMsIv8czB8jZcPmxCXFg==} engines: {node: '>= 0.4'} array.prototype.flatmap@1.3.3: resolution: {integrity: sha512-Y7Wt51eKJSyi80hFrJCePGGNo5ktJCslFuboqJsbf57CCPcm5zztluPlc4/aD8sWsKvlwatezpV4U1efk8kpjg==} engines: {node: '>= 0.4'} arraybuffer.prototype.slice@1.0.4: resolution: {integrity: sha512-BNoCY6SXXPQ7gF2opIP4GBE+Xw7U+pHMYKuzjgCN3GwiaIR09UUeKfheyIry77QtrCBlC0KK0q5/TER/tYh3PQ==} engines: {node: '>= 0.4'} assertion-error@2.0.1: resolution: {integrity: sha512-Izi8RQcffqCeNVgFigKli1ssklIbpHnCYc6AknXGYoB6grJqyeby7jv12JUQgmTAnIDnbck1uxksT4dzN3PWBA==} engines: {node: '>=12'} ast-types-flow@0.0.8: resolution: {integrity: sha512-OH/2E5Fg20h2aPrbe+QL8JZQFko0YZaF+j4mnQ7BGhfavO7OpSLa8a0y9sBwomHdSbkhTS8TQNayBfnW5DwbvQ==} async-function@1.0.0: resolution: {integrity: sha512-hsU18Ae8CDTR6Kgu9DYf0EbCr/a5iGL0rytQDobUcdpYOKokk8LEjVphnXkDkgpi0wYVsqrXuP0bZxJaTqdgoA==} engines: {node: '>= 0.4'} available-typed-arrays@1.0.7: resolution: {integrity: sha512-wvUjBtSGN7+7SjNpq/9M2Tg350UZD3q62IFZLbRAR1bSMlCo1ZaeW+BJ+D090e4hIIZLBcTDWe4Mh4jvUDajzQ==} engines: {node: '>= 0.4'} axe-core@4.12.1: resolution: {integrity: sha512-s7iGf5GaVMxEG0ENN9x+xTr7GFZCb1ZP/1uATUpCEK2X78nDB3RwbtFCo9pGAf9ru+VwoQ464DkaLEeRM08wJA==} engines: {node: '>=4'} axobject-query@4.1.0: resolution: {integrity: sha512-qIj0G9wZbMGNLjLmg1PT6v2mE9AH2zlnADJD/2tC6E00hgmhUOfEB6greHPAfLRSufHqROIUTkw6E+M3lH0PTQ==} engines: {node: '>= 0.4'} balanced-match@1.0.2: resolution: {integrity: sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==} balanced-match@4.0.4: resolution: {integrity: sha512-BLrgEcRTwX2o6gGxGOCNyMvGSp35YofuYzw9h1IMTRmKqttAZZVU67bdb9Pr2vUHA8+j3i2tJfjO6C6+4myGTA==} engines: {node: 18 || 20 || >=22} baseline-browser-mapping@2.10.42: resolution: {integrity: sha512-c/jurFrDLyui7o1J86yLkRu4LMsTYcBohveus7/I2Hzdn9KIP2bdJPTue/lR1KH46enoPbD77GKeSYNdyPoD3Q==} engines: {node: '>=6.0.0'} hasBin: true before-after-hook@4.0.0: resolution: {integrity: sha512-q6tR3RPqIB1pMiTRMFcZwuG5T8vwp+vUvEG0vuI6B+Rikh5BfPp2fQ82c925FOs+b0lcFQ8CFrL+KbilfZFhOQ==} bottleneck@2.19.5: resolution: {integrity: sha512-VHiNCbI1lKdl44tGrhNfU3lup0Tj/ZBMJB5/2ZbNXRCPuRCO7ed2mgcK4r17y+KB2EfuYuRaVlwNbAeaWGSpbw==} brace-expansion@1.1.16: resolution: {integrity: sha512-IDw48K2/2kRkg9LdJxurvq3lV3aBgq0REY89duEqFRthjlPdXHKMj7EnQOXVckxzgisinf3nHfrcE2FufFLXMw==} brace-expansion@5.0.7: resolution: {integrity: sha512-7oFy703dxfY3/NLxC1fh2SUCQ0H9rmAY+5EpDVfXjUTTs+HEwR2nYaqLv+GWcTsumwxPfiz6CzCNkwXwBUwqCA==} engines: {node: 18 || 20 || >=22} braces@3.0.3: resolution: {integrity: sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==} engines: {node: '>=8'} browserslist@4.28.5: resolution: {integrity: sha512-Cu2E6QejHWzuDMTkuwgpABFgDfZrXLQq5V13YOACZx4mFAG4IwGTbTfHPMr4WtxlHoXSM8FIuRwYYCz5XiabaQ==} engines: {node: ^6 || ^7 || ^8 || ^9 || ^10 || ^11 || ^12 || >=13.7} hasBin: true builtin-modules@5.3.0: resolution: {integrity: sha512-hMQUl2bUFG339QygPM97E+mc8OY1IAchORZxm4a/frcYwKzozMzRVDBwHW0NjOqGElLm2O37AVQE8ikxlZHrMQ==} engines: {node: '>=18.20'} call-bind-apply-helpers@1.0.2: resolution: {integrity: sha512-Sp1ablJ0ivDkSzjcaJdxEunN5/XvksFJ2sMBFfq6x0ryhQV/2b/KwFe21cMpmHtPOSij8K99/wSfoEuTObmuMQ==} engines: {node: '>= 0.4'} call-bind@1.0.9: resolution: {integrity: sha512-a/hy+pNsFUTR+Iz8TCJvXudKVLAnz/DyeSUo10I5yvFDQJBFU2s9uqQpoSrJlroHUKoKqzg+epxyP9lqFdzfBQ==} engines: {node: '>= 0.4'} call-bound@1.0.4: resolution: {integrity: sha512-+ys997U96po4Kx/ABpBCqhA9EuxJaQWDQg7295H4hBphv3IZg0boBKuwYpt4YXp6MZ5AmZQnU/tyMTlRpaSejg==} engines: {node: '>= 0.4'} callsites@3.1.0: resolution: {integrity: sha512-P8BjAsXvZS+VIDUI11hHCQEv74YT67YUi5JJFNWIqL235sBmjX4+qx9Muvls5ivyNENctx46xQLQ3aTuE7ssaQ==} engines: {node: '>=6'} caniuse-lite@1.0.30001803: resolution: {integrity: sha512-g/uHREV2ZpK9qMalCsWaxmA6ol+DX8GYhuf3T40RKoP+oL7vhRJh8LNt73PCjpnR6l14FzfPrB5Yux4PKm2meg==} chai@6.2.2: resolution: {integrity: sha512-NUPRluOfOiTKBKvWPtSD4PhFvWCqOi0BGStNWs57X9js7XGTprSmFoz5F0tWhR4WPjNeR9jXqdC7/UpSJTnlRg==} engines: {node: '>=18'} chalk@2.4.2: resolution: {integrity: sha512-Mti+f9lpJNcwF4tWV8/OrTTtF1gZi+f8FqlyAdouralcFWFQWF2+NgCHShjkCb+IFBLq9buZwE1xckQU4peSuQ==} engines: {node: '>=4'} chalk@4.1.2: resolution: {integrity: sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==} engines: {node: '>=10'} chalk@5.6.2: resolution: {integrity: sha512-7NzBL0rN6fMUW+f7A6Io4h40qQlG+xGmtMxfbnH/K7TAtt8JQWVQK+6g0UXKMeVJoyV5EkkNsErQ8pVD3bLHbA==} engines: {node: ^12.17.0 || ^14.13 || >=16.0.0} change-case@5.4.4: resolution: {integrity: sha512-HRQyTk2/YPEkt9TnUPbOpr64Uw3KOicFWPVBb+xiHvd6eBx/qPr9xqfBFDT8P2vWsvvz4jbEkfDe71W3VyNu2w==} char-regex@1.0.2: resolution: {integrity: sha512-kWWXztvZ5SBQV+eRgKFeh8q5sLuZY2+8WUIzlxWVTg+oGwY14qylx1KbKzHd8P6ZYkAg0xyIDU9JMHhyJMZ1jw==} engines: {node: '>=10'} character-entities-legacy@3.0.0: resolution: {integrity: sha512-RpPp0asT/6ufRm//AJVwpViZbGM/MkjQFxJccQRHmISF/22NBtsHqAWmL+/pmkPWoIUJdWyeVleTl1wydHATVQ==} character-entities@2.0.2: resolution: {integrity: sha512-shx7oQ0Awen/BRIdkjkvz54PnEEI/EjwXDSIZp86/KKdbafHh1Df/RYGBhn4hbe2+uKC9FnT5UCEdyPz3ai9hQ==} character-reference-invalid@2.0.1: resolution: {integrity: sha512-iBZ4F4wRbyORVsu0jPV7gXkOsGYjGHPmAyv+HiHG8gi5PtC9KI2j1+v8/tlibRvjoWX027ypmG/n0HtO5t7unw==} ci-info@4.4.0: resolution: {integrity: sha512-77PSwercCZU2Fc4sX94eF8k8Pxte6JAwL4/ICZLFjJLqegs7kCuAsqqj/70NQF6TvDpgFjkubQB2FW2ZZddvQg==} engines: {node: '>=8'} clean-regexp@1.0.0: resolution: {integrity: sha512-GfisEZEJvzKrmGWkvfhgzcz/BllN1USeqD2V6tg14OAOgaCD2Z/PUEuxnAZ/nPvmaHRG7a8y77p1T/IRQ4D1Hw==} engines: {node: '>=4'} clean-stack@2.2.0: resolution: {integrity: sha512-4diC9HaTE+KRAMWhDhrGOECgWZxoevMc5TlkObMqNSsVU62PYzXZ/SMTjzyGAFF1YusgxGcSWTEXBhp0CPwQ1A==} engines: {node: '>=6'} clean-stack@5.3.0: resolution: {integrity: sha512-9ngPTOhYGQqNVSfeJkYXHmF7AGWp4/nN5D/QqNQs3Dvxd1Kk/WpjHfNujKHYUQ/5CoGyOyFNoWSPk5afzP0QVg==} engines: {node: '>=14.16'} cli-cursor@5.0.0: resolution: {integrity: sha512-aCj4O5wKyszjMmDT4tZj93kxyydN/K5zPWSCe6/0AV/AA1pqe5ZBIw0a2ZfPQV7lL5/yb5HsUreJ6UFAF1tEQw==} engines: {node: '>=18'} cli-highlight@2.1.11: resolution: {integrity: sha512-9KDcoEVwyUXrjcJNvHD0NFc/hiwe/WPVYIleQh2O1N2Zro5gWJZ/K+3DGn8w8P/F6FxOgzyC5bxDyHIgCSPhGg==} engines: {node: '>=8.0.0', npm: '>=5.0.0'} hasBin: true cli-table3@0.6.5: resolution: {integrity: sha512-+W/5efTR7y5HRD7gACw9yQjqMVvEMLBHmboM/kPWam+H+Hmyrgjh6YncVKK122YZkXrLudzTuAukUw9FnMf7IQ==} engines: {node: 10.* || >= 12.*} cli-truncate@5.2.0: resolution: {integrity: sha512-xRwvIOMGrfOAnM1JYtqQImuaNtDEv9v6oIYAs4LIHwTiKee8uwvIi363igssOC0O5U04i4AlENs79LQLu9tEMw==} engines: {node: '>=20'} cliui@7.0.4: resolution: {integrity: sha512-OcRE68cOsVMXp1Yvonl/fzkQOyjLSu/8bhPDfQt0e0/Eb283TKP20Fs2MqoPsr9SwA595rRCA+QMzYc9nBP+JQ==} cliui@9.0.1: resolution: {integrity: sha512-k7ndgKhwoQveBL+/1tqGJYNz097I7WOvwbmmU2AR5+magtbjPWQTS1C5vzGkBC8Ym8UWRzfKUzUUqFLypY4Q+w==} engines: {node: '>=20'} color-convert@1.9.3: resolution: {integrity: sha512-QfAUtd+vFdAtFQcC8CCyYt1fYWxSqAiK2cSD6zDB8N3cpsEBAvRxp9zOGg6G/SHHJYAT88/az/IuDGALsNVbGg==} color-convert@2.0.1: resolution: {integrity: sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==} engines: {node: '>=7.0.0'} color-name@1.1.3: resolution: {integrity: sha512-72fSenhMw2HZMTVHeCA9KCmpEIbzWiQsjN+BHcBbS9vr1mtt+vJjPdksIBNUmKAW8TFUDPJK5SUU3QhE9NEXDw==} color-name@1.1.4: resolution: {integrity: sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==} commander@15.0.0: resolution: {integrity: sha512-z67u4ZhzCL/Tydu1lJARtEZYWbWaN7oYLHbsuzocr6y4N6WZAagG3RQ4FW61V1/0+jImpj293XfrcYnd1qxtPg==} engines: {node: '>=22.12.0'} commander@8.3.0: resolution: {integrity: sha512-OkTL9umf+He2DZkUq8f8J9of7yL6RJKI24dVITBmNfZBmri9zYZQrKkuXiKhyfPSu8tUhnVBB1iKXevvnlR4Ww==} engines: {node: '>= 12'} comment-parser@1.4.6: resolution: {integrity: sha512-ObxuY6vnbWTN6Od72xfwN9DbzC7Y2vv8u1Soi9ahRKL37gb6y1qk6/dgjs+3JWuXJHWvsg3BXIwzd/rkmAwavg==} engines: {node: '>= 12.0.0'} comment-parser@1.4.7: resolution: {integrity: sha512-0h+uSNtQGW3D98eQt3jJ8L06Fves8hncB4V/PKdw/Qb8Hnk19VaKuTr55UNRYiSoVa7WwrFls+rh3ux9agmkeQ==} engines: {node: '>= 12.0.0'} compare-func@2.0.0: resolution: {integrity: sha512-zHig5N+tPWARooBnb0Zx1MFcdfpyJrfTJ3Y5L+IFvUm8rM74hHz66z0gw0x4tijh5CorKkKUCnW82R2vmpeCRA==} concat-map@0.0.1: resolution: {integrity: sha512-/Srv4dswyQNBfohGpz9o6Yb3Gz3SrUDqBH5rTuhGR7ahtlbYKnVxw2bCFMRljaA7EXHaXZ8wsHdodFvbkhKmqg==} config-chain@1.1.13: resolution: {integrity: sha512-qj+f8APARXHrM0hraqXYb2/bOVSV4PvJQlNZ/DVj0QrmNM2q2euizkeuVckQ57J+W0mRH6Hvi+k50M4Jul2VRQ==} content-type@2.0.0: resolution: {integrity: sha512-j/O/d7GcZCyNl7/hwZAb606rzqkyvaDctLmckbxLzHvFBzTJHuGEdodATcP3yIRoDrLHkIATJuvzbFlp/ki2cQ==} engines: {node: '>=18'} conventional-changelog-angular@8.3.1: resolution: {integrity: sha512-6gfI3otXK5Ph5DfCOI1dblr+kN3FAm5a97hYoQkqNZxOaYa5WKfXH+AnpsmS+iUH2mgVC2Cg2Qw9m5OKcmNrIg==} engines: {node: '>=18'} conventional-changelog-writer@8.4.0: resolution: {integrity: sha512-HHBFkk1EECxxmCi4CTu091iuDpQv5/OavuCUAuZmrkWpmYfyD816nom1CvtfXJ/uYfAAjavgHvXHX291tSLK8g==} engines: {node: '>=18'} hasBin: true conventional-commits-filter@5.0.0: resolution: {integrity: sha512-tQMagCOC59EVgNZcC5zl7XqO30Wki9i9J3acbUvkaosCT6JX3EeFwJD7Qqp4MCikRnzS18WXV3BLIQ66ytu6+Q==} engines: {node: '>=18'} conventional-commits-parser@6.4.0: resolution: {integrity: sha512-tvRg7FIBNlyPzjdG8wWRlPHQJJHI7DylhtRGeU9Lq+JuoPh5BKpPRX83ZdLrvXuOSu5Eo/e7SzOQhU4Hd2Miuw==} engines: {node: '>=18'} hasBin: true convert-hrtime@5.0.0: resolution: {integrity: sha512-lOETlkIeYSJWcbbcvjRKGxVMXJR+8+OQb/mTPbA4ObPMytYIsUbuOE0Jzy60hjARYszq1id0j8KgVhC+WGZVTg==} engines: {node: '>=12'} convert-source-map@2.0.0: resolution: {integrity: sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg==} core-js-compat@3.49.0: resolution: {integrity: sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA==} core-util-is@1.0.3: resolution: {integrity: sha512-ZQBvi1DcpJ4GDqanjucZ2Hj3wEO5pZDS89BWbkcrvdxksJorwUDDZamX9ldFkp9aw2lmBDLgkObEA4DWNJ9FYQ==} cosmiconfig@9.0.2: resolution: {integrity: sha512-gtTZxTDau1wL7Y7zifc2dd8jHSK/k6BTx/2Xp/BpdlAdnlYWFVt7qhJqgwi7637yRwRQ3qL4ZidbB4I8tA5VOg==} engines: {node: '>=14'} peerDependencies: typescript: '>=4.9.5' peerDependenciesMeta: typescript: optional: true cross-spawn@7.0.6: resolution: {integrity: sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA==} engines: {node: '>= 8'} crypto-random-string@4.0.0: resolution: {integrity: sha512-x8dy3RnvYdlUcPOjkEHqozhiwzKNSq7GcPuXFbnyMOCHxX8V3OgIg/pYuabl2sbUPfIJaeAQB7PMOK8DFIdoRA==} engines: {node: '>=12'} damerau-levenshtein@1.0.8: resolution: {integrity: sha512-sdQSFB7+llfUcQHUQO3+B8ERRj0Oa4w9POWMI/puGtuf7gFywGmkaLCElnudfTiKZV+NvHqL0ifzdrI8Ro7ESA==} data-view-buffer@1.0.2: resolution: {integrity: sha512-EmKO5V3OLXh1rtK2wgXRansaK1/mtVdTUEiEI0W8RkvgT05kfxaH29PliLnpLP73yYO6142Q72QNa8Wx/A5CqQ==} engines: {node: '>= 0.4'} data-view-byte-length@1.0.2: resolution: {integrity: sha512-tuhGbE6CfTM9+5ANGf+oQb72Ky/0+s3xKUpHvShfiz2RxMFgFPjsXuRLBVMtvMs15awe45SRb83D6wH4ew6wlQ==} engines: {node: '>= 0.4'} data-view-byte-offset@1.0.1: resolution: {integrity: sha512-BS8PfmtDGnrgYdOonGZQdLZslWIeCGFP9tpan0hi1Co2Zr2NKADsvGYA8XxuG/4UWgJ6Cjtv+YJnB6MM69QGlQ==} engines: {node: '>= 0.4'} debug@4.4.3: resolution: {integrity: sha512-RGwwWnwQvkVfavKVt22FGLw+xYSdzARwm0ru6DhTVA3umU5hZc28V3kO4stgYryrTlLpuvgI9GiijltAjNbcqA==} engines: {node: '>=6.0'} peerDependencies: supports-color: '*' peerDependenciesMeta: supports-color: optional: true decode-named-character-reference@1.3.0: resolution: {integrity: sha512-GtpQYB283KrPp6nRw50q3U9/VfOutZOe103qlN7BPP6Ad27xYnOIWv4lPzo8HCAL+mMZofJ9KEy30fq6MfaK6Q==} deep-extend@0.6.0: resolution: {integrity: sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==} engines: {node: '>=4.0.0'} deep-is@0.1.4: resolution: {integrity: sha512-oIPzksmTg4/MriiaYGO+okXDT7ztn/w3Eptv/+gSIdMdKsJo0u4CfYNFJPy+4SKMuCqGw2wxnA+URMg3t8a/bQ==} define-data-property@1.1.4: resolution: {integrity: sha512-rBMvIzlpA8v6E+SJZoo++HAYqsLrkg7MSfIinMPFhmkorw7X+dOXVJQs+QT69zGkzMyfDnIMN2Wid1+NbL3T+A==} engines: {node: '>= 0.4'} define-properties@1.2.1: resolution: {integrity: sha512-8QmQKqEASLd5nx0U1B1okLElbUuuttJ/AnYmRXbbbGDWh6uS208EjD4Xqq/I9wK7u0v6O08XhTWnt5XtEbR6Dg==} engines: {node: '>= 0.4'} dequal@2.0.3: resolution: {integrity: sha512-0je+qPKHEMohvfRTCEo3CrPG6cAzAYgmzKyxRiYSSDkS6eGJdyVJm7WaYA5ECaAD9wLB2T4EEeymA5aFVcYXCA==} engines: {node: '>=6'} detect-libc@2.1.2: resolution: {integrity: sha512-Btj2BOOO83o3WyH59e8MgXsxEQVcarkUOpEYrubB0urwnN10yQ364rsiByU11nZlqWYZm05i/of7io4mzihBtQ==} engines: {node: '>=8'} devlop@1.1.0: resolution: {integrity: sha512-RWmIqhcFf1lRYBvNmr7qTNuyCt/7/ns2jbpp1+PalgE/rDQcBT0fioSMUpJ93irlUhC5hrg4cYqe6U+0ImW0rA==} dir-glob@3.0.1: resolution: {integrity: sha512-WkrWp9GR4KXfKGYzOLmTuGVi1UWFfws377n9cc55/tb6DuqyF6pcQ5AbiHEshaDpY9v6oaSr2XCDidGmMwdzIA==} engines: {node: '>=8'} dom-accessibility-api@0.5.16: resolution: {integrity: sha512-X7BJ2yElsnOJ30pZF4uIIDfBEVgF4XEBxL9Bxhy6dnrm5hkzqmsWHGTiHqRiITNhMyFLyAiWndIJP7Z1NTteDg==} dot-prop@5.3.0: resolution: {integrity: sha512-QM8q3zDe58hqUqjraQOmzZ1LIH9SWQJTlEKCH4kJ2oQvLZk7RbQXvtDM2XEq3fwkV9CCvvH4LA0AV+ogFsBM2Q==} engines: {node: '>=8'} dunder-proto@1.0.1: resolution: {integrity: sha512-KIN/nDJBQRcXw0MLVhZE9iQHmG68qAVIBg9CqmUYjmQIhgij9U5MFvrqkUL5FbtyyzZuOeOt0zdeRe4UY7ct+A==} engines: {node: '>= 0.4'} duplexer2@0.1.4: resolution: {integrity: sha512-asLFVfWWtJ90ZyOUHMqk7/S2w2guQKxUI2itj3d92ADHhxUSbCMGi1f1cBcJ7xM1To+pE/Khbwo1yuNbMEPKeA==} electron-to-chromium@1.5.389: resolution: {integrity: sha512-cEto7aeOqBfU1D+c5py5pE+ooscKE75JifxLBdFUZsqAxRS6y7kebtxAZvICszSl05gPjYHDTjY+lXpyGvpJbg==} emoji-regex@10.6.0: resolution: {integrity: sha512-toUI84YS5YmxW219erniWD0CIVOo46xGKColeNQRgOzDorgBi1v4D71/OFzgD9GO2UGKIv1C3Sp8DAn0+j5w7A==} emoji-regex@8.0.0: resolution: {integrity: sha512-MSjYzcWNOA0ewAHpz0MxpYFvwg6yjy1NG3xteoqz644VCo/RPgnr1/GGt+ic3iJTzQ8Eu3TdM14SawnVUmGE6A==} emoji-regex@9.2.2: resolution: {integrity: sha512-L18DaJsXSUk2+42pv8mLs5jJT2hqFkFE4j21wOmgbUqsZ2hL72NsUU785g9RXgo3s0ZNgVl42TiHp3ZtOv/Vyg==} emojilib@2.4.0: resolution: {integrity: sha512-5U0rVMU5Y2n2+ykNLQqMoqklN9ICBT/KsvC1Gz6vqHbz2AXXGkG+Pm5rMWk/8Vjrr/mY9985Hi8DYzn1F09Nyw==} entities@4.5.0: resolution: {integrity: sha512-V0hjH4dGPh9Ao5p0MoRY6BVqtwCjhz6vI5LT8AJ55H+4g9/4vbHx1I54fS0XuclLhDHArPQCiMjDxjaL8fPxhw==} engines: {node: '>=0.12'} env-ci@11.2.0: resolution: {integrity: sha512-D5kWfzkmaOQDioPmiviWAVtKmpPT4/iJmMVQxWxMPJTFyTkdc5JQUfc5iXEeWxcOdsYTKSAiA/Age4NUOqKsRA==} engines: {node: ^18.17 || >=20.6.1} env-paths@2.2.1: resolution: {integrity: sha512-+h1lkLKhZMTYjog1VEpJNG7NZJWcuc2DDk/qsqSTRRCOXiLjeQ1d1/udrUGhqMxUgAlwKNZ0cf2uqan5GLuS2A==} engines: {node: '>=6'} environment@1.1.0: resolution: {integrity: sha512-xUtoPkMggbz0MPyPiIWr1Kp4aeWJjDZ6SMvURhimjdZgsRuDplF5/s9hcgGhyXMhs+6vpnuoiZ2kFiu3FMnS8Q==} engines: {node: '>=18'} error-ex@1.3.4: resolution: {integrity: sha512-sqQamAnR14VgCr1A618A3sGrygcpK+HEbenA/HiEAkkUwcZIIB/tgWqHFxWgOyDh4nB4JCRimh79dR5Ywc9MDQ==} es-abstract-get@1.0.0: resolution: {integrity: sha512-6PMWXpdhshVvFp+FoWYs1EvG1Nj0tvk0dZM+XcK0xMEM1czRVcP6ohqPWHy6qPagSpC8j4+p89WXlT+xXJs/fg==} engines: {node: '>= 0.4'} es-abstract@1.24.2: resolution: {integrity: sha512-2FpH9Q5i2RRwyEP1AylXe6nYLR5OhaJTZwmlcP0dL/+JCbgg7yyEo/sEK6HeGZRf3dFpWwThaRHVApXSkW3xeg==} engines: {node: '>= 0.4'} es-define-property@1.0.1: resolution: {integrity: sha512-e3nRfgfUZ4rNGL232gUgX06QNyyez04KdjFrF+LTRoOXmrOgFKDg4BCdsjW8EnT69eqdYGmRpJwiPVYNrCaW3g==} engines: {node: '>= 0.4'} es-errors@1.3.0: resolution: {integrity: sha512-Zf5H2Kxt2xjTvbJvP2ZWLEICxA6j+hAmMzIlypy4xcBg1vKVnx89Wy0GbS+kf5cwCVFFzdCFh2XSCFNULS6csw==} engines: {node: '>= 0.4'} es-module-lexer@2.3.0: resolution: {integrity: sha512-KLdwQm2NvGLDkQDCGvmiQrhkd0JbMzXthwQAUgWjQuQdBLFa3eiBP5arXZyA+f8x+x7OXgud6bq2rxjGtHV2tw==} es-object-atoms@1.1.2: resolution: {integrity: sha512-HWcBoN6NileqtSydK2FqHbS/LoDd2pqrnQHLyJzBj4kOp/ky2MWMN694xOfkK8/SnUsW2DH7EfyVlydKCsm1Zw==} engines: {node: '>= 0.4'} es-set-tostringtag@2.1.0: resolution: {integrity: sha512-j6vWzfrGVfyXxge+O0x5sh6cvxAog0a/4Rdd2K36zCMV5eJ+/+tOAngRO8cODMNWbVRdVlmGZQL2YS3yR8bIUA==} engines: {node: '>= 0.4'} es-shim-unscopables@1.1.0: resolution: {integrity: sha512-d9T8ucsEhh8Bi1woXCf+TIKDIROLG5WCkxg8geBCbvk22kzwC5G2OnXVMO6FUsvQlgUUXQ2itephWDLqDzbeCw==} engines: {node: '>= 0.4'} es-to-primitive@1.3.4: resolution: {integrity: sha512-yPDz7wqpg1/mmHLmS3tcfTfbw5f1eryXvyghYBffGdERwe+mV7ZcWzTR8LR17Kvqt3qfPurjlonmnq3MKXIOXw==} engines: {node: '>= 0.4'} escalade@3.2.0: resolution: {integrity: sha512-WUj2qlxaQtO4g6Pq5c29GTcWGDyd8itL8zTlipgECz3JesAiiOKotd8JU6otB3PACgG6xkJUyVhboMS+bje/jA==} engines: {node: '>=6'} escape-string-regexp@1.0.5: resolution: {integrity: sha512-vbRorB5FUQWvla16U8R/qgaFIya2qGzwDrNmCZuYKrbdSUMG6I1ZCGQRefkRVhuOkIGVne7BQ35DSfo1qvJqFg==} engines: {node: '>=0.8.0'} escape-string-regexp@4.0.0: resolution: {integrity: sha512-TtpcNJ3XAzx3Gq8sWRzJaVajRs0uVxA2YAkdb1jm2YkPz4G6egUFAyA3n5vtEIZefPk5Wa4UXbKuS5fKkJWdgA==} engines: {node: '>=10'} escape-string-regexp@5.0.0: resolution: {integrity: sha512-/veY75JbMK4j1yjvuUxuVsiS/hr/4iHs9FTT6cgTexxdE0Ly/glccBAkloH/DofkjRbZU3bnoj38mOmhkZ0lHw==} engines: {node: '>=12'} eslint-config-decent@4.2.38: resolution: {integrity: sha512-HUgdCVEPPYisFj4UARR74v9JGdMHYVN4RG5i2bAFcDWdvjEyDB9oX68kNYBEDFTm+fbzpaaSN6ZNVbNvEi8QSg==} engines: {node: '>=22.13.0'} peerDependencies: '@stylistic/eslint-plugin': '>=5.0.0' '@typescript-eslint/eslint-plugin': '>=8.0.0' '@vitest/eslint-plugin': '>=1.0.0' eslint: ^9.0.0 || ^10.0.0 eslint-plugin-jsdoc: '>=62.0.0' eslint-plugin-react: '>=7.0.0' eslint-plugin-security: '>=4.0.0' eslint-plugin-testing-library: '>=7.0.0' eslint-plugin-unicorn: '>=63.0.0' oxlint: '>=1.53.0' oxlint-plugin-eslint: '>=1.52.0' oxlint-tsgolint: '>=0.1.0' typescript: '>=5.5.0' peerDependenciesMeta: '@stylistic/eslint-plugin': optional: true '@typescript-eslint/eslint-plugin': optional: true '@vitest/eslint-plugin': optional: true eslint-plugin-jsdoc: optional: true eslint-plugin-react: optional: true eslint-plugin-security: optional: true eslint-plugin-testing-library: optional: true eslint-plugin-unicorn: optional: true oxlint: optional: true oxlint-plugin-eslint: optional: true oxlint-tsgolint: optional: true eslint-config-prettier@10.1.8: resolution: {integrity: sha512-82GZUjRS0p/jganf6q1rEO25VSoHH0hKPCTrgillPjdI/3bgBhAE1QzHrHTizjpRvy6pGAvKjDJtk2pF9NDq8w==} hasBin: true peerDependencies: eslint: '>=7.0.0' eslint-import-context@0.1.9: resolution: {integrity: sha512-K9Hb+yRaGAGUbwjhFNHvSmmkZs9+zbuoe3kFQ4V1wYjrepUFYM2dZAfNtjbbj3qsPfUfsA68Bx/ICWQMi+C8Eg==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} peerDependencies: unrs-resolver: ^1.0.0 peerDependenciesMeta: unrs-resolver: optional: true eslint-plugin-import-x@4.16.2: resolution: {integrity: sha512-rM9K8UBHcWKpzQzStn1YRN2T5NvdeIfSVoKu/lKF41znQXHAUcBbYXe5wd6GNjZjTrP7viQ49n1D83x/2gYgIw==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: '@typescript-eslint/utils': ^8.56.0 eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 eslint-import-resolver-node: '*' peerDependenciesMeta: '@typescript-eslint/utils': optional: true eslint-import-resolver-node: optional: true eslint-plugin-jsdoc@62.9.0: resolution: {integrity: sha512-PY7/X4jrVgoIDncUmITlUqK546Ltmx/Pd4Hdsu4CvSjryQZJI2mEV4vrdMufyTetMiZ5taNSqvK//BTgVUlNkA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 || ^10.0.0 eslint-plugin-jsx-a11y@6.10.2: resolution: {integrity: sha512-scB3nz4WmG75pV8+3eRUQOHZlNSUhFNq37xnpgRkCCELU3XMvXAxLk1eqWWyE22Ki4Q01Fnsw9BA3cJHDPgn2Q==} engines: {node: '>=4.0'} peerDependencies: eslint: ^3 || ^4 || ^5 || ^6 || ^7 || ^8 || ^9 eslint-plugin-prettier@5.5.5: resolution: {integrity: sha512-hscXkbqUZ2sPithAuLm5MXL+Wph+U7wHngPBv9OMWwlP8iaflyxpjTYZkmdgB4/vPIhemRlBEoLrH7UC1n7aUw==} engines: {node: ^14.18.0 || >=16.0.0} peerDependencies: '@types/eslint': '>=8.0.0' eslint: '>=8.0.0' eslint-config-prettier: '>= 7.0.0 <10.0.0 || >=10.1.0' prettier: '>=3.0.0' peerDependenciesMeta: '@types/eslint': optional: true eslint-config-prettier: optional: true eslint-plugin-promise@7.2.1: resolution: {integrity: sha512-SWKjd+EuvWkYaS+uN2csvj0KoP43YTu7+phKQ5v+xw6+A0gutVX2yqCeCkC3uLCJFiPfR2dD8Es5L7yUsmvEaA==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^7.0.0 || ^8.0.0 || ^9.0.0 eslint-plugin-react-hooks@7.1.1: resolution: {integrity: sha512-f2I7Gw6JbvCexzIInuSbZpfdQ44D7iqdWX01FKLvrPgqxoE7oMj8clOfto8U6vYiz4yd5oKu39rRSVOe1zRu0g==} engines: {node: '>=18'} peerDependencies: eslint: ^3.0.0 || ^4.0.0 || ^5.0.0 || ^6.0.0 || ^7.0.0 || ^8.0.0-0 || ^9.0.0 || ^10.0.0 eslint-plugin-security@4.0.1: resolution: {integrity: sha512-/lZCkOxPOWaf1jXAqgICrS8St3BMBccIPvhOSUYuV6VCr1o5nFVG998FnTLt6w2Nxb8Uo0nM8fzmnhp+GY/aEg==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-plugin-unicorn@64.0.0: resolution: {integrity: sha512-rNZwalHh8i0UfPlhNwg5BTUO1CMdKNmjqe+TgzOTZnpKoi8VBgsW7u9qCHIdpxEzZ1uwrJrPF0uRb7l//K38gA==} engines: {node: ^20.10.0 || >=21.0.0} peerDependencies: eslint: '>=9.38.0' eslint-scope@9.1.2: resolution: {integrity: sha512-xS90H51cKw0jltxmvmHy2Iai1LIqrfbw57b79w/J7MfvDfkIkFZ+kj6zC3BjtUwh150HsSSdxXZcsuv72miDFQ==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint-visitor-keys@3.4.3: resolution: {integrity: sha512-wpc+LXeiyiisxPlEkUzU6svyS1frIO3Mgxj1fdy7Pm8Ygzguax2N3Fa/D/ag1WqbOprdI+uY6wMUl8/a2G+iag==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} eslint-visitor-keys@4.2.1: resolution: {integrity: sha512-Uhdk5sfqcee/9H/rCOJikYz67o0a2Tw2hGRPOG2Y1R2dg7brRe1uG0yaNQDHu+TO/uQPF/5eCapvYSmHUjt7JQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} eslint-visitor-keys@5.0.1: resolution: {integrity: sha512-tD40eHxA35h0PEIZNeIjkHoDR4YjjJp34biM0mDvplBe//mB+IHCqHDGV7pxF+7MklTvighcCPPZC7ynWyjdTA==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} eslint@10.6.0: resolution: {integrity: sha512-6lVbcqSodALYo+4ELD0heG6lFiFxnLMuLkiMi2qV8LMp54N8tE8FT1GMH+ev4Ti00nFjNze2+Su6DsV5OQW3Dg==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} hasBin: true peerDependencies: jiti: '*' peerDependenciesMeta: jiti: optional: true espree@10.4.0: resolution: {integrity: sha512-j6PAQ2uUr79PZhBjP5C5fhl8e39FmRnOjsD5lGnWrFU8i2G776tBK7+nP8KuQUTTyAZUwfQqXAgrVH5MbH9CYQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} espree@11.2.0: resolution: {integrity: sha512-7p3DrVEIopW1B1avAGLuCSh1jubc01H2JHc8B4qqGblmg5gI9yumBgACjWo4JlIc04ufug4xJ3SQI8HkS/Rgzw==} engines: {node: ^20.19.0 || ^22.13.0 || >=24} esquery@1.7.0: resolution: {integrity: sha512-Ap6G0WQwcU/LHsvLwON1fAQX9Zp0A2Y6Y/cJBl9r/JbW90Zyg4/zbG6zzKa2OTALELarYHmKu0GhpM5EO+7T0g==} engines: {node: '>=0.10'} esrecurse@4.3.0: resolution: {integrity: sha512-KmfKL3b6G+RXvP8N1vr3Tq1kL/oCFgn2NYXEtqP8/L3pKapUA4G8cFVaoF3SU323CD4XypR/ffioHmkti6/Tag==} engines: {node: '>=4.0'} estraverse@5.3.0: resolution: {integrity: sha512-MMdARuVEQziNTeJD8DgMqmhwR11BRQ/cBP+pLtYdSTnf3MIO8fFeiINEbX36ZdNlfU/7A9f3gUw49B3oQsvwBA==} engines: {node: '>=4.0'} estree-walker@3.0.3: resolution: {integrity: sha512-7RUKfXgSMMkzt6ZuXmqapOurLGPPfgj6l9uRZ7lRGolvk0y2yocc35LdcxKC5PQZdn2DMqioAQ2NoWcrTKmm6g==} esutils@2.0.3: resolution: {integrity: sha512-kVscqXk4OCp68SZ0dkgEKVi6/8ij300KBWTJq32P/dYeWTSwK41WyTxalN1eRmA5Z9UU/LX9D7FWSmV9SAYx6g==} engines: {node: '>=0.10.0'} eventemitter3@5.0.4: resolution: {integrity: sha512-mlsTRyGaPBjPedk6Bvw+aqbsXDtoAyAzm5MO7JgU+yVRyMQ5O8bD4Kcci7BS85f93veegeCPkL8R4GLClnjLFw==} execa@5.1.1: resolution: {integrity: sha512-8uSpZZocAZRBAPIEINJj3Lo9HyGitllczc27Eh5YYojjMFMn8yHMDMaUHE2Jqfq05D/wucwI4JGURyXt1vchyg==} engines: {node: '>=10'} execa@8.0.1: resolution: {integrity: sha512-VyhnebXciFV2DESc+p6B+y0LjSm0krU4OgJN44qFAhBY0TJ+1V61tYD2+wHusZ6F9n5K+vl8k0sTy7PEfV4qpg==} engines: {node: '>=16.17'} execa@9.6.1: resolution: {integrity: sha512-9Be3ZoN4LmYR90tUoVu2te2BsbzHfhJyfEiAVfz7N5/zv+jduIfLrV2xdQXOHbaD6KgpGdO9PRPM1Y4Q9QkPkA==} engines: {node: ^18.19.0 || >=20.5.0} expect-type@1.4.0: resolution: {integrity: sha512-KfYbmpRm0VbLjEvVa9yGwCi9GI34xvi7A/HXYWQO65CSD2u3MczUJSuwXKFIxlGsgBQizV9q5J9NHj4VG0n+pA==} engines: {node: '>=12.0.0'} fast-deep-equal@3.1.3: resolution: {integrity: sha512-f3qQ9oQy9j2AhBe/H9VC91wLmKBCCU/gDOnKNAYG5hswO7BLKj09Hc5HYNz9cGI++xlpDCIgDaitVs03ATR84Q==} fast-diff@1.3.0: resolution: {integrity: sha512-VxPP4NqbUjj6MaAOafWeUn2cXWLcCtljklUtZf0Ind4XQ+QPtmA0b18zZy0jIQx+ExRVCR/ZQpBmik5lXshNsw==} fast-glob@3.3.1: resolution: {integrity: sha512-kNFPyjhh5cKjrUltxs+wFx+ZkbRaxxmZ+X0ZU31SOsxCEtP9VPgtq2teZw1DebupL5GmDaNQ6yKMMVcM41iqDg==} engines: {node: '>=8.6.0'} fast-json-stable-stringify@2.1.0: resolution: {integrity: sha512-lhd/wF+Lk98HZoTCtlVraHtfh5XYijIjalXck7saUtuanSDyLMxnHhSXEDJqHxD7msR8D0uCmqlkwjCV8xvwHw==} fast-levenshtein@2.0.6: resolution: {integrity: sha512-DCXu6Ifhqcks7TZKY3Hxp3y6qphY5SJZmrWMDrKcERSOXWQdMhU9Ig/PYrzyw/ul9jOIyh0N4M0tbC5hodg8dw==} fastq@1.20.1: resolution: {integrity: sha512-GGToxJ/w1x32s/D2EKND7kTil4n8OVk/9mycTc4VDza13lOvpUZTGX3mFSCtV9ksdGBVzvsyAVLM6mHFThxXxw==} fdir@6.5.0: resolution: {integrity: sha512-tIbYtZbucOs0BRGqPJkshJUYdL+SDH7dVM8gjy+ERp3WAUjLEFJE+02kanyHtwjWOnwrKYBiwAmM0p4kLJAnXg==} engines: {node: '>=12.0.0'} peerDependencies: picomatch: ^3 || ^4 peerDependenciesMeta: picomatch: optional: true figures@2.0.0: resolution: {integrity: sha512-Oa2M9atig69ZkfwiApY8F2Yy+tzMbazyvqv21R0NsSC8floSOC09BbT1ITWAdoMGQvJ/aZnR1KMwdx9tvHnTNA==} engines: {node: '>=4'} figures@6.1.0: resolution: {integrity: sha512-d+l3qxjSesT4V7v2fh+QnmFnUWv9lSpjarhShNTgBOfA0ttejbQUAlHLitbjkoRiDulW0OPoQPYIGhIC8ohejg==} engines: {node: '>=18'} file-entry-cache@8.0.0: resolution: {integrity: sha512-XXTUwCvisa5oacNGRP9SfNtYBNAMi+RPwBFmblZEF7N7swHYQS6/Zfk7SRwx4D5j3CH211YNRco1DEMNVfZCnQ==} engines: {node: '>=16.0.0'} fill-range@7.1.1: resolution: {integrity: sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==} engines: {node: '>=8'} find-up-simple@1.0.1: resolution: {integrity: sha512-afd4O7zpqHeRyg4PfDQsXmlDe2PfdHtJt6Akt8jOWaApLOZk5JXs6VMR29lz03pRe9mpykrRCYIYxaJYcfpncQ==} engines: {node: '>=18'} find-up@2.1.0: resolution: {integrity: sha512-NWzkk0jSJtTt08+FBFMvXoeZnOJD+jTtsRmBYbAIzJdX6l7dLgR7CTubCM5/eDdPUBvLCeVasP1brfVR/9/EZQ==} engines: {node: '>=4'} find-up@5.0.0: resolution: {integrity: sha512-78/PXT1wlLLDgTzDs7sjq9hzz0vXD+zn+7wypEe4fXQxCmdmqfGsEPQxmiCSQI3ajFV91bVSsvNtrJRiW6nGng==} engines: {node: '>=10'} find-versions@6.0.0: resolution: {integrity: sha512-2kCCtc+JvcZ86IGAz3Z2Y0A1baIz9fL31pH/0S1IqZr9Iwnjq8izfPtrCyQKO6TLMPELLsQMre7VDqeIKCsHkA==} engines: {node: '>=18'} flat-cache@4.0.1: resolution: {integrity: sha512-f7ccFPK3SXFHpx15UIGyRJ/FJQctuKZ0zVuN3frBo4HnK3cay9VEW0R6yPYFHC0AgqhukPzKjq22t5DmAyqGyw==} engines: {node: '>=16'} flatted@3.4.2: resolution: {integrity: sha512-PjDse7RzhcPkIJwy5t7KPWQSZ9cAbzQXcafsetQoD7sOJRQlGikNbx7yZp2OotDnJyrDcbyRq3Ttb18iYOqkxA==} for-each@0.3.5: resolution: {integrity: sha512-dKx12eRCVIzqCxFGplyFKJMPvLEWgmNtUrpTiJIR5u97zEhRG8ySrtboPHZXx7daLxQVrl643cTzbab2tkQjxg==} engines: {node: '>= 0.4'} fs-extra@11.3.6: resolution: {integrity: sha512-w8ZNZr2mKIc7qeNaQ9AVPT1+iFaI+Avd4xudVOvdDJ8VytREi1Ft5Ih7hd9jjehod8vAM5GMsfQ/TpPf4EyoEA==} engines: {node: '>=14.14'} fsevents@2.3.3: resolution: {integrity: sha512-5xoDfX+fL7faATnagmWPpbFtwh/R77WmMMqqHGS65C3vvB0YHrgF+B1YmZ3441tMj5n63k0212XNoJwzlhffQw==} engines: {node: ^8.16.0 || ^10.6.0 || >=11.0.0} os: [darwin] function-bind@1.1.2: resolution: {integrity: sha512-7XHNxH7qX9xG5mIwxkhumTox/MIRNcOgDrxWsMt2pAr23WHp6MrRlN7FBSFpCpr+oVO0F744iUgR82nJMfG2SA==} function-timeout@1.0.2: resolution: {integrity: sha512-939eZS4gJ3htTHAldmyyuzlrD58P03fHG49v2JfFXbV6OhvZKRC9j2yAtdHw/zrp2zXHuv05zMIy40F0ge7spA==} engines: {node: '>=18'} function.prototype.name@1.2.0: resolution: {integrity: sha512-jObKIik1P2QjPHP5nz5BaOtUlfgS0fWo8IUByNXkM+o+02sJOi94em77GwJKQSJ3gfPHdgzLNrHc1uokV4P/ew==} engines: {node: '>= 0.4'} functions-have-names@1.2.3: resolution: {integrity: sha512-xckBUXyTIqT97tq2x2AMb+g163b5JFysYk0x4qxNFwbfQkmNZoiRHb6sPzI9/QV33WeuvVYBUIiD4NzNIyqaRQ==} generator-function@2.0.1: resolution: {integrity: sha512-SFdFmIJi+ybC0vjlHN0ZGVGHc3lgE0DxPAT0djjVg+kjOnSqclqmj0KQ7ykTOLP6YxoqOvuAODGdcHJn+43q3g==} engines: {node: '>= 0.4'} gensync@1.0.0-beta.2: resolution: {integrity: sha512-3hN7NaskYvMDLQY55gnW3NQ+mesEAepTqlg+VEbj7zzqEMBVNhzcGYYeqFo/TlYz6eQiFcp1HcsCZO+nGgS8zg==} engines: {node: '>=6.9.0'} get-caller-file@2.0.5: resolution: {integrity: sha512-DyFP3BM/3YHTQOCUL/w0OZHR0lpKeGrxotcHWcqNEdnltqFwXVfhEBQ94eIo34AfQpo0rGki4cyIiftY06h2Fg==} engines: {node: 6.* || 8.* || >= 10.*} get-east-asian-width@1.6.0: resolution: {integrity: sha512-QRbvDIbx6YklUe6RxeTeleMR0yv3cYH6PsPZHcnVn7xv7zO1BHN8r0XETu8n6Ye3Q+ahtSarc3WgtNWmehIBfA==} engines: {node: '>=18'} get-intrinsic@1.3.0: resolution: {integrity: sha512-9fSjSaos/fRIVIp+xSJlE6lfwhES7LNtKaCBIamHsjr2na1BiABJPo0mOjjz8GJDURarmCPGqaiVg5mfjb98CQ==} engines: {node: '>= 0.4'} get-proto@1.0.1: resolution: {integrity: sha512-sTSfBjoXBp89JvIKIefqw7U2CCebsc74kiY6awiGogKtoSGbgjYE/G/+l9sF3MWFPNc9IcoOC4ODfKHfxFmp0g==} engines: {node: '>= 0.4'} get-stream@6.0.1: resolution: {integrity: sha512-ts6Wi+2j3jQjqi70w5AlN8DFnkSwC+MqmxEzdEALB2qXZYV3X/b1CTfgPLGJNMeAWxdPfU8FO1ms3NUfaHCPYg==} engines: {node: '>=10'} get-stream@8.0.1: resolution: {integrity: sha512-VaUJspBffn/LMCJVoMvSAdmscJyS1auj5Zulnn5UoYcY531UWmdwhRWkcGKnGU93m5HSXP9LP2usOryrBtQowA==} engines: {node: '>=16'} get-stream@9.0.1: resolution: {integrity: sha512-kVCxPF3vQM/N0B1PmoqVUqgHP+EeVjmZSQn+1oCRPxd2P21P2F19lIgbR3HBosbB1PUhOAoctJnfEn2GbN2eZA==} engines: {node: '>=18'} get-symbol-description@1.1.0: resolution: {integrity: sha512-w9UMqWwJxHNOvoNzSJ2oPF5wvYcvP7jUvYzhp67yEhTi17ZDBBC1z9pTdGuzjD+EFIqLSYRweZjqfiPzQ06Ebg==} engines: {node: '>= 0.4'} get-tsconfig@4.14.0: resolution: {integrity: sha512-yTb+8DXzDREzgvYmh6s9vHsSVCHeC0G3PI5bEXNBHtmshPnO+S5O7qgLEOn0I5QvMy6kpZN8K1NKGyilLb93wA==} git-log-parser@1.2.1: resolution: {integrity: sha512-PI+sPDvHXNPl5WNOErAK05s3j0lgwUzMN6o8cyQrDaKfT3qd7TmNJKeXX+SknI5I0QhG5fVPAEwSY4tRGDtYoQ==} glob-parent@5.1.2: resolution: {integrity: sha512-AOIgSQCepiJYwP3ARnGx+5VnTu2HBYdzbGP45eLw1vr3zB3vZLeyed1sC9hnbcOc9/SrMyM5RPQrkGz4aS9Zow==} engines: {node: '>= 6'} glob-parent@6.0.2: resolution: {integrity: sha512-XxwI8EOhVQgWp6iDL+3b0r86f4d6AX6zSU55HfB4ydCEuXLXc5FcYeOu+nnGftS4TEju/11rt4KJPTMgbfmv4A==} engines: {node: '>=10.13.0'} globals@17.5.0: resolution: {integrity: sha512-qoV+HK2yFl/366t2/Cb3+xxPUo5BuMynomoDmiaZBIdbs+0pYbjfZU+twLhGKp4uCZ/+NbtpVepH5bGCxRyy2g==} engines: {node: '>=18'} globals@17.7.0: resolution: {integrity: sha512-Czmyns5dUsq4seFBR/Kdydhmo8y9kC79hiSkPn0YcGtNnYWnrgt0vjrSjx9tspoDGWm2CMarffRuLjM4xUz8xg==} engines: {node: '>=18'} globalthis@1.0.4: resolution: {integrity: sha512-DpLKbNU4WylpxJykQujfCcwYWiV/Jhm50Goo0wrVILAv5jOr9d+H+UR3PhSCD2rCCEIg0uc+G+muBTwD54JhDQ==} engines: {node: '>= 0.4'} gopd@1.2.0: resolution: {integrity: sha512-ZUKRh6/kUFoAiTAtTYPZJ3hw9wNxx+BIBOijnlG9PnrJsCcSjs1wyyD6vJpaYtgnzDrKYRSqf3OO6Rfa93xsRg==} engines: {node: '>= 0.4'} graceful-fs@4.2.10: resolution: {integrity: sha512-9ByhssR2fPVsNZj478qUUbKfmL0+t5BDVyjShtyZZLiK7ZDAArFFfopyOTj0M05wE2tJPisA4iTnnXl2YoPvOA==} graceful-fs@4.2.11: resolution: {integrity: sha512-RbJ5/jmFcNNCcDV5o9eTnBLJ/HszWV0P73bc+Ff4nS/rJj+YaS6IGyiOL0VoBYX+l1Wrl3k63h/KrH+nhJ0XvQ==} handlebars@4.7.9: resolution: {integrity: sha512-4E71E0rpOaQuJR2A3xDZ+GM1HyWYv1clR58tC8emQNeQe3RH7MAzSbat+V0wG78LQBo6m6bzSG/L4pBuCsgnUQ==} engines: {node: '>=0.4.7'} hasBin: true has-bigints@1.1.0: resolution: {integrity: sha512-R3pbpkcIqv2Pm3dUwgjclDRVmWpTJW2DcMzcIhEXEx1oh/CEMObMm3KLmRJOdvhM7o4uQBnwr8pzRK2sJWIqfg==} engines: {node: '>= 0.4'} has-flag@3.0.0: resolution: {integrity: sha512-sKJf1+ceQBr4SMkvQnBDNDtf4TXpVhVGateu0t918bl30FnbE2m4vNLX+VWe/dpjlb+HugGYzW7uQXH98HPEYw==} engines: {node: '>=4'} has-flag@4.0.0: resolution: {integrity: sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==} engines: {node: '>=8'} has-property-descriptors@1.0.2: resolution: {integrity: sha512-55JNKuIW+vq4Ke1BjOTjM2YctQIvCT7GFzHwmfZPGo5wnrgkid0YQtnAleFSqumZm4az3n2BS+erby5ipJdgrg==} has-proto@1.2.0: resolution: {integrity: sha512-KIL7eQPfHQRC8+XluaIw7BHUwwqL19bQn4hzNgdr+1wXoU0KKj6rufu47lhY7KbJR2C6T6+PfyN0Ea7wkSS+qQ==} engines: {node: '>= 0.4'} has-symbols@1.1.0: resolution: {integrity: sha512-1cDNdwJ2Jaohmb3sg4OmKaMBwuC48sYni5HUw2DvsC8LjGTLK9h+eb1X6RyuOHe4hT0ULCW68iomhjUoKUqlPQ==} engines: {node: '>= 0.4'} has-tostringtag@1.0.2: resolution: {integrity: sha512-NqADB8VjPFLM2V0VvHUewwwsw0ZWBaIdgo+ieHtK3hasLz4qeCRjYcqfB6AQrBggRKppKF8L52/VqdVsO47Dlw==} engines: {node: '>= 0.4'} hasown@2.0.4: resolution: {integrity: sha512-T2UbfbBEF32wiepXIsMlTW9+dDYC6wMh/t/vYA4tuOMKqWz/n3vr1NFSxQiyP+zk2mXsoMA/i/7qV6LKut1t1A==} engines: {node: '>= 0.4'} hermes-estree@0.25.1: resolution: {integrity: sha512-0wUoCcLp+5Ev5pDW2OriHC2MJCbwLwuRx+gAqMTOkGKJJiBCLjtrvy4PWUGn6MIVefecRpzoOZ/UV6iGdOr+Cw==} hermes-parser@0.25.1: resolution: {integrity: sha512-6pEjquH3rqaI6cYAXYPcz9MS4rY6R4ngRgrgfDshRptUZIc3lw0MCIJIGDj9++mfySOuPTHB4nrSW99BCvOPIA==} highlight.js@10.7.3: resolution: {integrity: sha512-tzcUFauisWKNHaRkN4Wjl/ZA07gENAjFl3J/c480dprkGTg5EQstgaNFqBfUqCq54kZRIEcreTsAgF/m2quD7A==} hook-std@4.0.0: resolution: {integrity: sha512-IHI4bEVOt3vRUDJ+bFA9VUJlo7SzvFARPNLw75pqSmAOP2HmTWfFJtPvLBrDrlgjEYXY9zs7SFdHPQaJShkSCQ==} engines: {node: '>=20'} hosted-git-info@7.0.2: resolution: {integrity: sha512-puUZAUKT5m8Zzvs72XWy3HtvVbTWljRE66cP60bxJzAqf2DgICo7lYTY2IHUmLnNpjYvw5bvmoHvPc0QO2a62w==} engines: {node: ^16.14.0 || >=18.0.0} hosted-git-info@9.0.3: resolution: {integrity: sha512-Hc+ghLoSt6QaYZUv0WBiIvmMDZuZZ7oaDvdH8MbfOO4lOsxdXLEvuC6ePoGs9H1X9oCLyq6+NVN0MKqD+ydxyg==} engines: {node: ^20.17.0 || >=22.9.0} html-entities@2.6.0: resolution: {integrity: sha512-kig+rMn/QOVRvr7c86gQ8lWXq+Hkv6CbAH1hLu+RG338StTpE8Z0b44SDVaqVu7HGKf27frdmUYEs9hTUX/cLQ==} http-proxy-agent@9.1.0: resolution: {integrity: sha512-2NxoveTT58mjYT4n3RPTEfCZGLMbidoO8XEieXfpSYxu+PQJ1qpx4ypwH6N+uF9twBPIvRRgvkvW5HUTYWENig==} engines: {node: '>= 20'} https-proxy-agent@9.1.0: resolution: {integrity: sha512-ag87y7cJJ9/3+GxFr8Oy4O5faDsGRGnBGsJj/YjOSsSx/5eadKLYTMPlzuR6obgoCDDm0abAAZitXXQkMOPSpA==} engines: {node: '>= 20'} human-signals@2.1.0: resolution: {integrity: sha512-B4FFZ6q/T2jhhksgkbEW3HBvWIfDW85snkQgawt07S7J5QXTk6BkNV+0yAeZrM5QpMAdYlocGoljn0sJ/WQkFw==} engines: {node: '>=10.17.0'} human-signals@5.0.0: resolution: {integrity: sha512-AXcZb6vzzrFAUE61HnN4mpLqd/cSIwNQjtNWR0euPm6y0iqx3G4gOXaIDdtdDwZmhwe82LA6+zinmW4UBWVePQ==} engines: {node: '>=16.17.0'} human-signals@8.0.1: resolution: {integrity: sha512-eKCa6bwnJhvxj14kZk5NCPc6Hb6BdsU9DZcOnmQKSnO1VKrfV0zCvtttPZUsBvjmNDn8rpcJfpwSYnHBjc95MQ==} engines: {node: '>=18.18.0'} husky@9.1.7: resolution: {integrity: sha512-5gs5ytaNjBrh5Ow3zrvdUUY+0VxIuWVL4i9irt6friV+BqdCfmV11CQTWMiBYWHbXhco+J1kHfTOUkePhCDvMA==} engines: {node: '>=18'} hasBin: true ignore@5.3.2: resolution: {integrity: sha512-hsBTNUqQTDwkWtcdYI2i06Y/nUBEsNEDJKjWdigLvegy8kDuJAS8uRlpkkcQpyEXL0Z/pjDy5HBmMjRCJ2gq+g==} engines: {node: '>= 4'} ignore@7.0.6: resolution: {integrity: sha512-BAg6QkE8W+TuQLrrw0Ugr7HegXduRuuj8/ti2kSOc+jz1dmx8/WNcjr6XGnq5YpDWxFwwaavqD0+jIUOKelTsw==} engines: {node: '>= 4'} import-fresh@3.3.1: resolution: {integrity: sha512-TR3KfrTZTYLPB6jUjfx6MF9WcWrHL9su5TObK4ZkYgBdWKPOFoSoQIdEuTuR82pmtxH2spWG9h6etwfr1pLBqQ==} engines: {node: '>=6'} import-from-esm@2.0.0: resolution: {integrity: sha512-YVt14UZCgsX1vZQ3gKjkWVdBdHQ6eu3MPU1TBgL1H5orXe2+jWD006WCPPtOuwlQm10NuzOW5WawiF1Q9veW8g==} engines: {node: '>=18.20'} import-meta-resolve@4.2.0: resolution: {integrity: sha512-Iqv2fzaTQN28s/FwZAoFq0ZSs/7hMAHJVX+w8PZl3cY19Pxk6jFFalxQoIfW2826i/fDLXv8IiEZRIT0lDuWcg==} imurmurhash@0.1.4: resolution: {integrity: sha512-JmXMZ6wuvDmLiHEml9ykzqO6lwFbof0GG4IkcGaENdCRDDmMVnny7s5HsIgHCbaq0w2MyPhDqkhTUgS2LU2PHA==} engines: {node: '>=0.8.19'} indent-string@4.0.0: resolution: {integrity: sha512-EdDDZu4A2OyIK7Lr/2zG+w5jmbuk1DVBnEwREQvBzspBJkCEbRa8GxU1lghYcaGJCnRWibjDXlq779X1/y5xwg==} engines: {node: '>=8'} indent-string@5.0.0: resolution: {integrity: sha512-m6FAo/spmsW2Ab2fU35JTYwtOKa2yAwXSwgjSv1TJzh4Mh7mC3lzAOVLBprb72XsTrgkEIsl7YrFNAiDiRhIGg==} engines: {node: '>=12'} index-to-position@1.2.0: resolution: {integrity: sha512-Yg7+ztRkqslMAS2iFaU+Oa4KTSidr63OsFGlOrJoW981kIYO3CGCS3wA95P1mUi/IVSJkn0D479KTJpVpvFNuw==} engines: {node: '>=18'} inherits@2.0.4: resolution: {integrity: sha512-k/vGaX4/Yla3WzyMCvTQOXYeIHvqOKtnqBduzTHpzpQZzAskKMhZ2K+EnBiSM9zGSoIFeMpXKxa4dYeZIQqewQ==} ini@1.3.8: resolution: {integrity: sha512-JV/yugV2uzW5iMRSiZAyDtQd+nxtUnjeLt0acNdw98kKLrvuRVyB80tsREOE7yvGVgalhZ6RNXCmEHkUKBKxew==} ini@4.1.3: resolution: {integrity: sha512-X7rqawQBvfdjS10YU1y1YVreA3SsLrW9dX2CewP2EbBJM4ypVNLDkO5y04gejPwKIY9lR+7r9gn3rFPt/kmWFg==} engines: {node: ^14.17.0 || ^16.13.0 || >=18.0.0} internal-slot@1.1.0: resolution: {integrity: sha512-4gd7VpWNQNB4UKKCFFVcp1AVv+FMOgs9NKzjHKusc8jTMhd5eL1NqQqOpE0KzMds804/yHlglp3uxgluOqAPLw==} engines: {node: '>= 0.4'} is-alphabetical@2.0.1: resolution: {integrity: sha512-FWyyY60MeTNyeSRpkM2Iry0G9hpr7/9kD40mD/cGQEuilcZYS4okz8SN2Q6rLCJ8gbCt6fN+rC+6tMGS99LaxQ==} is-alphanumerical@2.0.1: resolution: {integrity: sha512-hmbYhX/9MUMF5uh7tOXyK/n0ZvWpad5caBA17GsC6vyuCqaWliRG5K1qS9inmUhEMaOBIW7/whAnSwveW/LtZw==} is-array-buffer@3.0.5: resolution: {integrity: sha512-DDfANUiiG2wC1qawP66qlTugJeL5HyzMpfr8lLK+jMQirGzNod0B12cFB/9q838Ru27sBwfw78/rdoU7RERz6A==} engines: {node: '>= 0.4'} is-arrayish@0.2.1: resolution: {integrity: sha512-zz06S8t0ozoDXMG+ube26zeCTNXcKIPJZJi8hBrF4idCLms4CG9QtK7qBl1boi5ODzFpjswb5JPmHCbMpjaYzg==} is-async-function@2.1.1: resolution: {integrity: sha512-9dgM/cZBnNvjzaMYHVoxxfPj2QXt22Ev7SuuPrs+xav0ukGB0S6d4ydZdEiM48kLx5kDV+QBPrpVnFyefL8kkQ==} engines: {node: '>= 0.4'} is-bigint@1.1.0: resolution: {integrity: sha512-n4ZT37wG78iz03xPRKJrHTdZbe3IicyucEtdRsV5yglwc3GyUfbAfpSeD0FJ41NbUNSt5wbhqfp1fS+BgnvDFQ==} engines: {node: '>= 0.4'} is-boolean-object@1.2.2: resolution: {integrity: sha512-wa56o2/ElJMYqjCjGkXri7it5FbebW5usLw/nPmCMs5DeZ7eziSYZhSmPRn0txqeW4LnAmQQU7FgqLpsEFKM4A==} engines: {node: '>= 0.4'} is-builtin-module@5.0.0: resolution: {integrity: sha512-f4RqJKBUe5rQkJ2eJEJBXSticB3hGbN9j0yxxMQFqIW89Jp9WYFtzfTcRlstDKVUTRzSOTLKRfO9vIztenwtxA==} engines: {node: '>=18.20'} is-callable@1.2.7: resolution: {integrity: sha512-1BC0BVFhS/p0qtw6enp8e+8OD0UrK0oFLztSjNzhcKA3WDuJxxAPXzPuPtKkjEY9UUoEWlX/8fgKeu2S8i9JTA==} engines: {node: '>= 0.4'} is-data-view@1.0.2: resolution: {integrity: sha512-RKtWF8pGmS87i2D6gqQu/l7EYRlVdfzemCJN/P3UOs//x1QE7mfhvzHIApBTRf7axvT6DMGwSwBXYCT0nfB9xw==} engines: {node: '>= 0.4'} is-date-object@1.1.0: resolution: {integrity: sha512-PwwhEakHVKTdRNVOw+/Gyh0+MzlCl4R6qKvkhuvLtPMggI1WAHt9sOwZxQLSGpUaDnrdyDsomoRgNnCfKNSXXg==} engines: {node: '>= 0.4'} is-decimal@2.0.1: resolution: {integrity: sha512-AAB9hiomQs5DXWcRB1rqsxGUstbRroFOPPVAomNk/3XHR5JyEZChOyTWe2oayKnsSsr/kcGqF+z6yuH6HHpN0A==} is-document.all@1.0.0: resolution: {integrity: sha512-+XSoyS05OdBbhFuELhgTCpFNHkpBOJqtsZfUFFpe5QTw+9Sjbh8zitxhQkYAo6wV7e1Vb8cAPvpCk9jGam/82g==} engines: {node: '>= 0.4'} is-extglob@2.1.1: resolution: {integrity: sha512-SbKbANkN603Vi4jEZv49LeVJMn4yGwsbzZworEoyEiutsN3nJYdbO36zfhGJ6QEDpOZIFkDtnq5JRxmvl3jsoQ==} engines: {node: '>=0.10.0'} is-finalizationregistry@1.1.1: resolution: {integrity: sha512-1pC6N8qWJbWoPtEjgcL2xyhQOP491EQjeUo3qTKcmV8YSDDJrOepfG8pcC7h/QgnQHYSv0mJ3Z/ZWxmatVrysg==} engines: {node: '>= 0.4'} is-fullwidth-code-point@3.0.0: resolution: {integrity: sha512-zymm5+u+sCsSWyD9qNaejV3DFvhCKclKdizYaJUuHA83RLjb7nSuGnddCHGv0hk+KY7BMAlsWeK4Ueg6EV6XQg==} engines: {node: '>=8'} is-fullwidth-code-point@5.1.0: resolution: {integrity: sha512-5XHYaSyiqADb4RnZ1Bdad6cPp8Toise4TzEjcOYDHZkTCbKgiUl7WTUCpNWHuxmDt91wnsZBc9xinNzopv3JMQ==} engines: {node: '>=18'} is-generator-function@1.1.2: resolution: {integrity: sha512-upqt1SkGkODW9tsGNG5mtXTXtECizwtS2kA161M+gJPc1xdb/Ax629af6YrTwcOeQHbewrPNlE5Dx7kzvXTizA==} engines: {node: '>= 0.4'} is-glob@4.0.3: resolution: {integrity: sha512-xelSayHH36ZgE7ZWhli7pW34hNbNl8Ojv5KVmkJD4hBdD3th8Tfk9vYasLM+mXWOZhFkgZfxhLSnrwRr4elSSg==} engines: {node: '>=0.10.0'} is-hexadecimal@2.0.1: resolution: {integrity: sha512-DgZQp241c8oO6cA1SbTEWiXeoxV42vlcJxgH+B3hi1AiqqKruZR3ZGF8In3fj4+/y/7rHvlOZLZtgJ/4ttYGZg==} is-map@2.0.3: resolution: {integrity: sha512-1Qed0/Hr2m+YqxnM09CjA2d/i6YZNfF6R2oRAOj36eUdS6qIV/huPJNSEpKbupewFs+ZsJlxsjjPbc0/afW6Lw==} engines: {node: '>= 0.4'} is-negative-zero@2.0.3: resolution: {integrity: sha512-5KoIu2Ngpyek75jXodFvnafB6DJgr3u8uuK0LEZJjrU19DrMD3EVERaR8sjz8CCGgpZvxPl9SuE1GMVPFHx1mw==} engines: {node: '>= 0.4'} is-number-object@1.1.1: resolution: {integrity: sha512-lZhclumE1G6VYD8VHe35wFaIif+CTy5SJIi5+3y4psDgWu4wPDoBhF8NxUOinEc7pHgiTsT6MaBb92rKhhD+Xw==} engines: {node: '>= 0.4'} is-number@7.0.0: resolution: {integrity: sha512-41Cifkg6e8TylSpdtTpeLVMqvSBEVzTttHvERD741+pnZ8ANv0004MRL43QKPDlK9cGvNp6NZWZUBlbGXYxxng==} engines: {node: '>=0.12.0'} is-obj@2.0.0: resolution: {integrity: sha512-drqDG3cbczxxEJRoOXcOjtdp1J/lyp1mNn0xaznRs8+muBhgQcrnbspox5X5fOw0HnMnbfDzvnEMEtqDEJEo8w==} engines: {node: '>=8'} is-plain-obj@4.1.0: resolution: {integrity: sha512-+Pgi+vMuUNkJyExiMBt5IlFoMyKnr5zhJ4Uspz58WOhBF5QoIZkFyNHIbBAtHwzVAgk5RtndVNsDRN61/mmDqg==} engines: {node: '>=12'} is-regex@1.2.1: resolution: {integrity: sha512-MjYsKHO5O7mCsmRGxWcLWheFqN9DJ/2TmngvjKXihe6efViPqc274+Fx/4fYj/r03+ESvBdTXK0V6tA3rgez1g==} engines: {node: '>= 0.4'} is-set@2.0.3: resolution: {integrity: sha512-iPAjerrse27/ygGLxw+EBR9agv9Y6uLeYVJMu+QNCoouJ1/1ri0mGrcWpfCqFZuzzx3WjtwxG098X+n4OuRkPg==} engines: {node: '>= 0.4'} is-shared-array-buffer@1.0.4: resolution: {integrity: sha512-ISWac8drv4ZGfwKl5slpHG9OwPNty4jOWPRIhBpxOoD+hqITiwuipOQ2bNthAzwA3B4fIjO4Nln74N0S9byq8A==} engines: {node: '>= 0.4'} is-stream@2.0.1: resolution: {integrity: sha512-hFoiJiTl63nn+kstHGBtewWSKnQLpyb155KHheA1l39uvtO9nWIop1p3udqPcUd/xbF1VLMO4n7OI6p7RbngDg==} engines: {node: '>=8'} is-stream@3.0.0: resolution: {integrity: sha512-LnQR4bZ9IADDRSkvpqMGvt/tEJWclzklNgSw48V5EAaAeDd6qGvN8ei6k5p0tvxSR171VmGyHuTiAOfxAbr8kA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} is-stream@4.0.1: resolution: {integrity: sha512-Dnz92NInDqYckGEUJv689RbRiTSEHCQ7wOVeALbkOz999YpqT46yMRIGtSNl2iCL1waAZSx40+h59NV/EwzV/A==} engines: {node: '>=18'} is-string@1.1.1: resolution: {integrity: sha512-BtEeSsoaQjlSPBemMQIrY1MY0uM6vnS1g5fmufYOtnxLGUZM2178PKbhsk7Ffv58IX+ZtcvoGwccYsh0PglkAA==} engines: {node: '>= 0.4'} is-symbol@1.1.1: resolution: {integrity: sha512-9gGx6GTtCQM73BgmHQXfDmLtfjjTUDSyoxTCbp5WtoixAhfgsDirWIcVQ/IHpvI5Vgd5i/J5F7B9cN/WlVbC/w==} engines: {node: '>= 0.4'} is-typed-array@1.1.15: resolution: {integrity: sha512-p3EcsicXjit7SaskXHs1hA91QxgTw46Fv6EFKKGS5DRFLD8yKnohjF3hxoju94b/OcMZoQukzpPpBE9uLVKzgQ==} engines: {node: '>= 0.4'} is-unicode-supported@2.1.0: resolution: {integrity: sha512-mE00Gnza5EEB3Ds0HfMyllZzbBrmLOX3vfWoj9A9PEnTfratQ/BcaJOuMhnkhjXvb2+FkY3VuHqtAGpTPmglFQ==} engines: {node: '>=18'} is-weakmap@2.0.2: resolution: {integrity: sha512-K5pXYOm9wqY1RgjpL3YTkF39tni1XajUIkawTLUo9EZEVUFga5gSQJF8nNS7ZwJQ02y+1YCNYcMh+HIf1ZqE+w==} engines: {node: '>= 0.4'} is-weakref@1.1.1: resolution: {integrity: sha512-6i9mGWSlqzNMEqpCp93KwRS1uUOodk2OJ6b+sq7ZPDSy2WuI5NFIxp/254TytR8ftefexkWn5xNiHUNpPOfSew==} engines: {node: '>= 0.4'} is-weakset@2.0.4: resolution: {integrity: sha512-mfcwb6IzQyOKTs84CQMrOwW4gQcaTOAWJ0zzJCl2WSPDrWk/OzDaImWFH3djXhb24g4eudZfLRozAvPGw4d9hQ==} engines: {node: '>= 0.4'} isarray@1.0.0: resolution: {integrity: sha512-VLghIWNM6ELQzo7zwmcg0NmTVyWKYjvIeM83yjp0wRDTmUnrM678fQbcKBo6n2CJEF0szoG//ytg+TKla89ALQ==} isarray@2.0.5: resolution: {integrity: sha512-xHjhDr3cNBK0BzdUJSPXZntQUx/mwMS5Rw4A7lPJ90XGAO6ISP/ePDNuo0vhqOZU+UD5JoodwCAAoZQd3FeAKw==} isexe@2.0.0: resolution: {integrity: sha512-RHxMLp9lnKHGHRng9QFhRCMbYAcVpn69smSGcq3f36xjgVVWThj4qqLbTLlq7Ssj8B+fIQ1EuCEGI2lKsyQeIw==} isexe@4.0.0: resolution: {integrity: sha512-FFUtZMpoZ8RqHS3XeXEmHWLA4thH+ZxCv2lOiPIn1Xc7CxrqhWzNSDzD+/chS/zbYezmiwWLdQC09JdQKmthOw==} engines: {node: '>=20'} issue-parser@7.0.2: resolution: {integrity: sha512-7atWPjhGEIX3JEtMrOYd8TKzboYlq+5sNbdl9POiLYOI14G5HZiQbZP0Xj5EZdrufQVXfJlpTV0hys0CuxwxZw==} engines: {node: ^18.17 || >=20.6.1} java-properties@1.0.2: resolution: {integrity: sha512-qjdpeo2yKlYTH7nFdK0vbZWuTCesk4o63v5iVOlhMQPfuIZQfW/HI35SjfhA+4qpg36rnFSvUK5b1m+ckIblQQ==} engines: {node: '>= 0.6.0'} js-tokens@4.0.0: resolution: {integrity: sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ==} js-yaml@4.2.0: resolution: {integrity: sha512-ePWsvanv0DWuDRsW8dnt+R4jQ31SCRCQ7hhNcPXZPsoBZiemuZNYGf7adZdqX2D86j6rvKp3RpCxVTSb8WQlOw==} hasBin: true js-yaml@4.3.0: resolution: {integrity: sha512-1td788aAnnZ5qs7V2QIRl1owjtYpbKt749Y3xauqQgwIIGF/xXWz1wMTEBx5O3LK3lXLVuqXPdPxj2BoFHaW9Q==} hasBin: true jsdoc-type-pratt-parser@7.2.0: resolution: {integrity: sha512-dh140MMgjyg3JhJZY/+iEzW+NO5xR2gpbDFKHqotCmexElVntw7GjWjt511+C/Ef02RU5TKYrJo/Xlzk+OLaTw==} engines: {node: '>=20.0.0'} jsesc@3.1.0: resolution: {integrity: sha512-/sM3dO2FOzXjKQhJuo0Q173wf2KOo8t4I8vHy6lF9poUp7bKT0/NHE8fPX23PwfhnykfqnC2xRxOnVw5XuGIaA==} engines: {node: '>=6'} hasBin: true json-buffer@3.0.1: resolution: {integrity: sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==} json-parse-better-errors@1.0.2: resolution: {integrity: sha512-mrqyZKfX5EhL7hvqcV6WG1yYjnjeuYDzDhhcAAUrq8Po85NBQBJP+ZDUT75qZQ98IkUoBqdkExkukOU7Ts2wrw==} json-parse-even-better-errors@2.3.1: resolution: {integrity: sha512-xyFwyhro/JEof6Ghe2iz2NcXoj2sloNsWr/XsERDK/oiPCfaNhl5ONfp+jQdAZRQQ0IJWNzH9zIZF7li91kh2w==} json-parse-even-better-errors@6.0.0: resolution: {integrity: sha512-2/8adwnK1/+Fdjyts4r6wSpfANWw8zdNhU9U/Llk59c6O+DjSisPWPykwoL8gZmocP9Dy64S7oie2g+Mia123A==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} json-schema-traverse@0.4.1: resolution: {integrity: sha512-xbbCH5dCYU5T8LcEhhuh7HJ88HXuW3qsI3Y0zOZFKfZEHcpWiHU/Jxzk629Brsab/mMiHQti9wMP+845RPe3Vg==} json-stable-stringify-without-jsonify@1.0.1: resolution: {integrity: sha512-Bdboy+l7tA3OGW6FjyFHWkP5LuByj1Tk33Ljyq0axyzdk9//JSi2u3fP1QSmd1KNwq6VOKYGlAu87CisVir6Pw==} json-with-bigint@3.5.8: resolution: {integrity: sha512-eq/4KP6K34kwa7TcFdtvnftvHCD9KvHOGGICWwMFc4dOOKF5t4iYqnfLK8otCRCRv06FXOzGGyqE8h8ElMvvdw==} json5@2.2.3: resolution: {integrity: sha512-XmOWe7eyHYH14cLdVPoyg+GOH3rYX++KpzrylJwSW98t3Nk+U8XOl8FWKOgwtzdb8lXGf6zYwDUzeHMWfxasyg==} engines: {node: '>=6'} hasBin: true jsonc-parser@3.3.1: resolution: {integrity: sha512-HUgH65KyejrUFPvHFPbqOY0rsFip3Bo5wb4ngvdi1EpCYWUQDC5V+Y7mZws+DLkr4M//zQJoanu1SP+87Dv1oQ==} jsonfile@6.2.1: resolution: {integrity: sha512-zwOTdL3rFQ/lRdBnntKVOX6k5cKJwEc1HdilT71BWEu7J41gXIB2MRp+vxduPSwZJPWBxEzv4yH1wYLJGUHX4Q==} jsonpointer@5.0.1: resolution: {integrity: sha512-p/nXbhSEcu3pZRdkW1OfJhpsVtW1gd4Wa1fnQc9YLiTfAjn0312eMKimbdIQzuZl9aa9xUGaRlP9T/CJE/ditQ==} engines: {node: '>=0.10.0'} jsx-ast-utils@3.3.5: resolution: {integrity: sha512-ZZow9HBI5O6EPgSJLUb8n2NKgmVWTwCvHGwFuJlMjvLFqlGG6pjirPhtdsseaLZjSibD8eegzmYpUZwoIlj2cQ==} engines: {node: '>=4.0'} katex@0.16.47: resolution: {integrity: sha512-Eeo8Ys1doU1z+x8AZsPpQu+p/QcZBI5PeOo7QGQdy2x2m0MU/hYagBbGOmXwr5KVbEfVuWv9LpnQWeehogurjg==} hasBin: true keyv@4.5.4: resolution: {integrity: sha512-oxVHkHR/EJf2CNXnWxRLW6mg7JyCCUcG0DtEGmL2ctUo1PNTin1PUil+r/+4r5MpVgC/fn1kjsx7mjSujKqIpw==} language-subtag-registry@0.3.23: resolution: {integrity: sha512-0K65Lea881pHotoGEa5gDlMxt3pctLi2RplBb7Ezh4rRdLEOtgi7n4EwK9lamnUCkKBqaeKRVebTq6BAxSkpXQ==} language-tags@1.0.9: resolution: {integrity: sha512-MbjN408fEndfiQXbFQ1vnd+1NoLDsnQW41410oQBXiyXDMYH5z505juWa4KUE1LqxRC7DgOgZDbKLxHIwm27hA==} engines: {node: '>=0.10'} levn@0.4.1: resolution: {integrity: sha512-+bT2uH4E5LGE7h/n3evcS/sQlJXCpIp6ym8OWJ5eV6+67Dsql/LaaT7qJBAt2rzfoa/5QBGBhxDix1dMt2kQKQ==} engines: {node: '>= 0.8.0'} lightningcss-android-arm64@1.32.0: resolution: {integrity: sha512-YK7/ClTt4kAK0vo6w3X+Pnm0D2cf2vPHbhOXdoNti1Ga0al1P4TBZhwjATvjNwLEBCnKvjJc2jQgHXH0NEwlAg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [android] lightningcss-darwin-arm64@1.32.0: resolution: {integrity: sha512-RzeG9Ju5bag2Bv1/lwlVJvBE3q6TtXskdZLLCyfg5pt+HLz9BqlICO7LZM7VHNTTn/5PRhHFBSjk5lc4cmscPQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [darwin] lightningcss-darwin-x64@1.32.0: resolution: {integrity: sha512-U+QsBp2m/s2wqpUYT/6wnlagdZbtZdndSmut/NJqlCcMLTWp5muCrID+K5UJ6jqD2BFshejCYXniPDbNh73V8w==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [darwin] lightningcss-freebsd-x64@1.32.0: resolution: {integrity: sha512-JCTigedEksZk3tHTTthnMdVfGf61Fky8Ji2E4YjUTEQX14xiy/lTzXnu1vwiZe3bYe0q+SpsSH/CTeDXK6WHig==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [freebsd] lightningcss-linux-arm-gnueabihf@1.32.0: resolution: {integrity: sha512-x6rnnpRa2GL0zQOkt6rts3YDPzduLpWvwAF6EMhXFVZXD4tPrBkEFqzGowzCsIWsPjqSK+tyNEODUBXeeVHSkw==} engines: {node: '>= 12.0.0'} cpu: [arm] os: [linux] lightningcss-linux-arm64-gnu@1.32.0: resolution: {integrity: sha512-0nnMyoyOLRJXfbMOilaSRcLH3Jw5z9HDNGfT/gwCPgaDjnx0i8w7vBzFLFR1f6CMLKF8gVbebmkUN3fa/kQJpQ==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [glibc] lightningcss-linux-arm64-musl@1.32.0: resolution: {integrity: sha512-UpQkoenr4UJEzgVIYpI80lDFvRmPVg6oqboNHfoH4CQIfNA+HOrZ7Mo7KZP02dC6LjghPQJeBsvXhJod/wnIBg==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [linux] libc: [musl] lightningcss-linux-x64-gnu@1.32.0: resolution: {integrity: sha512-V7Qr52IhZmdKPVr+Vtw8o+WLsQJYCTd8loIfpDaMRWGUZfBOYEJeyJIkqGIDMZPwPx24pUMfwSxxI8phr/MbOA==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [glibc] lightningcss-linux-x64-musl@1.32.0: resolution: {integrity: sha512-bYcLp+Vb0awsiXg/80uCRezCYHNg1/l3mt0gzHnWV9XP1W5sKa5/TCdGWaR/zBM2PeF/HbsQv/j2URNOiVuxWg==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [linux] libc: [musl] lightningcss-win32-arm64-msvc@1.32.0: resolution: {integrity: sha512-8SbC8BR40pS6baCM8sbtYDSwEVQd4JlFTOlaD3gWGHfThTcABnNDBda6eTZeqbofalIJhFx0qKzgHJmcPTnGdw==} engines: {node: '>= 12.0.0'} cpu: [arm64] os: [win32] lightningcss-win32-x64-msvc@1.32.0: resolution: {integrity: sha512-Amq9B/SoZYdDi1kFrojnoqPLxYhQ4Wo5XiL8EVJrVsB8ARoC1PWW6VGtT0WKCemjy8aC+louJnjS7U18x3b06Q==} engines: {node: '>= 12.0.0'} cpu: [x64] os: [win32] lightningcss@1.32.0: resolution: {integrity: sha512-NXYBzinNrblfraPGyrbPoD19C1h9lfI/1mzgWYvXUTe414Gz/X1FD2XBZSZM7rRTrMA8JL3OtAaGifrIKhQ5yQ==} engines: {node: '>= 12.0.0'} lines-and-columns@1.2.4: resolution: {integrity: sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg==} linkify-it@5.0.2: resolution: {integrity: sha512-ONTm2jCMAVZjgQa/Fy1kScXsuOoF5NPTsoFBdE1KVIZ2vAh/r9+Bqo+0jINCBYnavTPQZz38QzFTme79ENoN3Q==} lint-staged@17.0.8: resolution: {integrity: sha512-B2P/d+jVW0UXOQ0MVMLrB/9ydA1P+zz6jYfdrbbEd9ur3S2rcbduFWKiUCC02Sm5hbC8nrm7y24WuYMG54HfxA==} engines: {node: '>=22.22.1'} hasBin: true listr2@10.2.2: resolution: {integrity: sha512-JtNtbZj8q5BnDMR7trpwvwk3RIrANtIVzEUm8w7amp6xelLgyuq+4WZoTH913XaQAoH/cNdYhaNzBPA2U3xbDw==} engines: {node: '>=22.13.0'} load-json-file@4.0.0: resolution: {integrity: sha512-Kx8hMakjX03tiGTLAIdJ+lL0htKnXjEZN6hk/tozf/WOuYGdZBJrZ+rCJRbVCugsjB3jMLn9746NsQIf5VjBMw==} engines: {node: '>=4'} locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} locate-path@6.0.0: resolution: {integrity: sha512-iPZK6eYjbxRu3uB4/WZ3EsEIMJFMqAoopl3R+zuq0UjcAm/MO6KCweDgPfP3elTztoKP3KtnVHxTn2NHBSDVUw==} engines: {node: '>=10'} lodash-es@4.18.1: resolution: {integrity: sha512-J8xewKD/Gk22OZbhpOVSwcs60zhd95ESDwezOFuA3/099925PdHJ7OFHNTGtajL3AlZkykD32HykiMo+BIBI8A==} lodash.capitalize@4.2.1: resolution: {integrity: sha512-kZzYOKspf8XVX5AvmQF94gQW0lejFVgb80G85bU4ZWzoJ6C03PQg3coYAUpSTpQWelrZELd3XWgHzw4Ck5kaIw==} lodash.escaperegexp@4.1.2: resolution: {integrity: sha512-TM9YBvyC84ZxE3rgfefxUWiQKLilstD6k7PTGt6wfbtXF8ixIJLOL3VYyV/z+ZiPLsVxAsKAFVwWlWeb2Y8Yyw==} lodash.isplainobject@4.0.6: resolution: {integrity: sha512-oSXzaWypCMHkPC3NvBEaPHf0KsA5mvPrOPgQWDsbg8n7orZ290M0BmC/jgRZ4vcJ6DTAhjrsSYgdsW/F+MFOBA==} lodash.isstring@4.0.1: resolution: {integrity: sha512-0wJxfxH1wgO3GrbuP+dTTk7op+6L41QCXbGINEmD+ny/G/eCqGzxyCsh7159S+mgDDcoarnBw6PC1PS5+wUGgw==} lodash.uniqby@4.7.0: resolution: {integrity: sha512-e/zcLx6CSbmaEgFHCA7BnoQKyCtKMxnuWrJygbwPs/AIn+IMKl66L8/s+wBUn5LRw2pZx3bUHibiV1b6aTWIww==} lodash@4.18.1: resolution: {integrity: sha512-dMInicTPVE8d1e5otfwmmjlxkZoUpiVLwyeTdUsi/Caj/gfzzblBcCE5sRHV/AsjuCmxWrte2TNGSYuCeCq+0Q==} log-update@6.1.0: resolution: {integrity: sha512-9ie8ItPR6tjY5uYJh8K/Zrv/RMZ5VOlOWvtZdEHYSTFKZfIBPQa9tOAEeAWhd+AnIneLJ22w5fjOYtoutpWq5w==} engines: {node: '>=18'} lru-cache@10.4.3: resolution: {integrity: sha512-JNAzZcXrCt42VGLuYz0zfAzDfAvJWW6AfYlDBQyDV5DClI2m5sAmK+OIO7s59XfsRsWHp02jAJrRadPRGTt6SQ==} lru-cache@11.5.2: resolution: {integrity: sha512-4pfM1Ff0x50o0tQwb5ucw/RzNyD0/YJME6IVcStalZuMWxdt3sR3huStTtxz4PUmvZfRguvDejasvQ2kifR11g==} engines: {node: 20 || >=22} lru-cache@5.1.1: resolution: {integrity: sha512-KpNARQA3Iwv+jTA0utUVVbrh+Jlrr1Fv0e56GGzAFOXN7dk/FviaDW8LHmK52DlcH4WP2n6gI8vN1aesBFgo9w==} lz-string@1.5.0: resolution: {integrity: sha512-h5bgJWpxJNswbU7qCrV0tIKQCaS3blPDrqKWx+QxzuzL1zGUzij9XCWLrSLsJPu5t+eWA/ycetzYAO5IOMcWAQ==} hasBin: true magic-string@0.30.21: resolution: {integrity: sha512-vd2F4YUyEXKGcLHoq+TEyCjxueSeHnFxyyjNp80yg0XV4vUhnDer/lvvlqM/arB5bXQN5K2/3oinyCRyx8T2CQ==} make-asynchronous@1.1.0: resolution: {integrity: sha512-ayF7iT+44LXdxJLTrTd3TLQpFDDvPCBxXxbv+pMUSuHA5Q8zyAfwkRP6aHHwNVFBUFWtxAHqwNJxF8vMZLAbVg==} engines: {node: '>=18'} markdown-it@14.2.0: resolution: {integrity: sha512-1TGiQiJVRQ3NPmZH6sx5Cfnmg6GQm9jvC1ch4TK511NjSJvjzKLzn5pPfZRNZkRPZP0HqCioSndqH8v2nRaWVQ==} hasBin: true markdownlint-cli@0.49.0: resolution: {integrity: sha512-vS5tWq5W91Gg33LD4pyAaXPclnz/sRvo6/RGOyDQjQ3eds2DkK6H4szUuE0M9TiRB/u/VBx1gtd9Ktrtx5WlSA==} engines: {node: '>=22'} hasBin: true markdownlint@0.41.0: resolution: {integrity: sha512-xMUI3ChBuRuxuLF4ENvCZyS8z/+Jly1coUcZwErKLIB3sDj7ojpaTBa1e9YVPhSN4jGEIjYGQCldbTJS/hqS+A==} engines: {node: '>=22'} marked-terminal@7.3.0: resolution: {integrity: sha512-t4rBvPsHc57uE/2nJOLmMbZCQ4tgAccAED3ngXQqW6g+TxA488JzJ+FK3lQkzBQOI1mRV/r/Kq+1ZlJ4D0owQw==} engines: {node: '>=16.0.0'} peerDependencies: marked: '>=1 <16' marked@15.0.12: resolution: {integrity: sha512-8dD6FusOQSrpv9Z1rdNMdlSgQOIP880DHqnohobOmYLElGEqAL/JvxvuxZO16r4HtjTlfPRDC1hbvxC9dPN2nA==} engines: {node: '>= 18'} hasBin: true math-intrinsics@1.1.0: resolution: {integrity: sha512-/IXtbwEk5HTPyEwyKX6hGkYXxM9nbj64B+ilVJnC/R6B0pH5G4V3b0pVbL7DBj4tkhBAppbQUlf6F6Xl9LHu1g==} engines: {node: '>= 0.4'} mdurl@2.0.0: resolution: {integrity: sha512-Lf+9+2r+Tdp5wXDXC4PcIBjTDtq4UKjCPMQhKIuzpJNW0b96kVqSwW0bT7FhRSfmAiFYgP+SCRvdrDozfh0U5w==} memorystream@0.3.1: resolution: {integrity: sha512-S3UwM3yj5mtUSEfP41UZmt/0SCoVYUcU1rkXv+BQ5Ig8ndL4sPoJNBUJERafdPb5jjHJGuMgytgKvKIf58XNBw==} engines: {node: '>= 0.10.0'} meow@13.2.0: resolution: {integrity: sha512-pxQJQzB6djGPXh08dacEloMFopsOqGVRKFPYvPOt9XDZ1HasbgDZA74CJGreSU4G3Ak7EFJGoiH2auq+yXISgA==} engines: {node: '>=18'} merge-stream@2.0.0: resolution: {integrity: sha512-abv/qOcuPfk3URPfDzmZU1LKmuw8kT+0nIHvKrKgFrwifol/doWcdA4ZqsWQ8ENrFKkd67Mfpo/LovbIUsbt3w==} merge2@1.4.1: resolution: {integrity: sha512-8q7VEgMJW4J8tcfVPy8g09NcQwZdbwFEqhe/WZkoIzjn/3TGDwtOCYtXGxA3O8tPzpczCCDgv+P2P5y00ZJOOg==} engines: {node: '>= 8'} micromark-core-commonmark@2.0.3: resolution: {integrity: sha512-RDBrHEMSxVFLg6xvnXmb1Ayr2WzLAWjeSATAoxwKYJV94TeNavgoIdA0a9ytzDSVzBy2YKFK+emCPOEibLeCrg==} micromark-extension-directive@4.0.0: resolution: {integrity: sha512-/C2nqVmXXmiseSSuCdItCMho7ybwwop6RrrRPk0KbOHW21JKoCldC+8rFOaundDoRBUWBnJJcxeA/Kvi34WQXg==} micromark-extension-gfm-autolink-literal@2.1.0: resolution: {integrity: sha512-oOg7knzhicgQ3t4QCjCWgTmfNhvQbDDnJeVu9v81r7NltNCVmhPy1fJRX27pISafdjL+SVc4d3l48Gb6pbRypw==} micromark-extension-gfm-footnote@2.1.0: resolution: {integrity: sha512-/yPhxI1ntnDNsiHtzLKYnE3vf9JZ6cAisqVDauhp4CEHxlb4uoOTxOCJ+9s51bIB8U1N1FJ1RXOKTIlD5B/gqw==} micromark-extension-gfm-table@2.1.1: resolution: {integrity: sha512-t2OU/dXXioARrC6yWfJ4hqB7rct14e8f7m0cbI5hUmDyyIlwv5vEtooptH8INkbLzOatzKuVbQmAYcbWoyz6Dg==} micromark-extension-math@3.1.0: resolution: {integrity: sha512-lvEqd+fHjATVs+2v/8kg9i5Q0AP2k85H0WUOwpIVvUML8BapsMvh1XAogmQjOCsLpoKRCVQqEkQBB3NhVBcsOg==} micromark-factory-destination@2.0.1: resolution: {integrity: sha512-Xe6rDdJlkmbFRExpTOmRj9N3MaWmbAgdpSrBQvCFqhezUn4AHqJHbaEnfbVYYiexVSs//tqOdY/DxhjdCiJnIA==} micromark-factory-label@2.0.1: resolution: {integrity: sha512-VFMekyQExqIW7xIChcXn4ok29YE3rnuyveW3wZQWWqF4Nv9Wk5rgJ99KzPvHjkmPXF93FXIbBp6YdW3t71/7Vg==} micromark-factory-space@2.0.1: resolution: {integrity: sha512-zRkxjtBxxLd2Sc0d+fbnEunsTj46SWXgXciZmHq0kDYGnck/ZSGj9/wULTV95uoeYiK5hRXP2mJ98Uo4cq/LQg==} micromark-factory-title@2.0.1: resolution: {integrity: sha512-5bZ+3CjhAd9eChYTHsjy6TGxpOFSKgKKJPJxr293jTbfry2KDoWkhBb6TcPVB4NmzaPhMs1Frm9AZH7OD4Cjzw==} micromark-factory-whitespace@2.0.1: resolution: {integrity: sha512-Ob0nuZ3PKt/n0hORHyvoD9uZhr+Za8sFoP+OnMcnWK5lngSzALgQYKMr9RJVOWLqQYuyn6ulqGWSXdwf6F80lQ==} micromark-util-character@2.1.1: resolution: {integrity: sha512-wv8tdUTJ3thSFFFJKtpYKOYiGP2+v96Hvk4Tu8KpCAsTMs6yi+nVmGh1syvSCsaxz45J6Jbw+9DD6g97+NV67Q==} micromark-util-chunked@2.0.1: resolution: {integrity: sha512-QUNFEOPELfmvv+4xiNg2sRYeS/P84pTW0TCgP5zc9FpXetHY0ab7SxKyAQCNCc1eK0459uoLI1y5oO5Vc1dbhA==} micromark-util-classify-character@2.0.1: resolution: {integrity: sha512-K0kHzM6afW/MbeWYWLjoHQv1sgg2Q9EccHEDzSkxiP/EaagNzCm7T/WMKZ3rjMbvIpvBiZgwR3dKMygtA4mG1Q==} micromark-util-combine-extensions@2.0.1: resolution: {integrity: sha512-OnAnH8Ujmy59JcyZw8JSbK9cGpdVY44NKgSM7E9Eh7DiLS2E9RNQf0dONaGDzEG9yjEl5hcqeIsj4hfRkLH/Bg==} micromark-util-decode-numeric-character-reference@2.0.2: resolution: {integrity: sha512-ccUbYk6CwVdkmCQMyr64dXz42EfHGkPQlBj5p7YVGzq8I7CtjXZJrubAYezf7Rp+bjPseiROqe7G6foFd+lEuw==} micromark-util-encode@2.0.1: resolution: {integrity: sha512-c3cVx2y4KqUnwopcO9b/SCdo2O67LwJJ/UyqGfbigahfegL9myoEFoDYZgkT7f36T0bLrM9hZTAaAyH+PCAXjw==} micromark-util-html-tag-name@2.0.1: resolution: {integrity: sha512-2cNEiYDhCWKI+Gs9T0Tiysk136SnR13hhO8yW6BGNyhOC4qYFnwF1nKfD3HFAIXA5c45RrIG1ub11GiXeYd1xA==} micromark-util-normalize-identifier@2.0.1: resolution: {integrity: sha512-sxPqmo70LyARJs0w2UclACPUUEqltCkJ6PhKdMIDuJ3gSf/Q+/GIe3WKl0Ijb/GyH9lOpUkRAO2wp0GVkLvS9Q==} micromark-util-resolve-all@2.0.1: resolution: {integrity: sha512-VdQyxFWFT2/FGJgwQnJYbe1jjQoNTS4RjglmSjTUlpUMa95Htx9NHeYW4rGDJzbjvCsl9eLjMQwGeElsqmzcHg==} micromark-util-sanitize-uri@2.0.1: resolution: {integrity: sha512-9N9IomZ/YuGGZZmQec1MbgxtlgougxTodVwDzzEouPKo3qFWvymFHWcnDi2vzV1ff6kas9ucW+o3yzJK9YB1AQ==} micromark-util-subtokenize@2.1.0: resolution: {integrity: sha512-XQLu552iSctvnEcgXw6+Sx75GflAPNED1qx7eBJ+wydBb2KCbRZe+NwvIEEMM83uml1+2WSXpBAcp9IUCgCYWA==} micromark-util-symbol@2.0.1: resolution: {integrity: sha512-vs5t8Apaud9N28kgCrRUdEed4UJ+wWNvicHLPxCa9ENlYuAY31M0ETy5y1vA33YoNPDFTghEbnh6efaE8h4x0Q==} micromark-util-types@2.0.2: resolution: {integrity: sha512-Yw0ECSpJoViF1qTU4DC6NwtC4aWGt1EkzaQB8KPPyCRR8z9TWeV0HbEFGTO+ZY1wB22zmxnJqhPyTpOVCpeHTA==} micromark@4.0.2: resolution: {integrity: sha512-zpe98Q6kvavpCr1NPVSCMebCKfD7CA2NqZ+rykeNhONIJBpc1tFKt9hucLGwha3jNTNI8lHpctWJWoimVF4PfA==} micromatch@4.0.8: resolution: {integrity: sha512-PXwfBhYu0hBCPw8Dn0E+WDYb7af3dSLVWKi3HGv84IdF4TyFoC0ysxFd0Goxw7nSv4T/PzEJQxsYsEiFCKo2BA==} engines: {node: '>=8.6'} mime@4.1.0: resolution: {integrity: sha512-X5ju04+cAzsojXKes0B/S4tcYtFAJ6tTMuSPBEn9CPGlrWr8Fiw7qYeLT0XyH80HSoAoqWCaz+MWKh22P7G1cw==} engines: {node: '>=16'} hasBin: true mimic-fn@2.1.0: resolution: {integrity: sha512-OqbOk5oEQeAZ8WXWydlu9HJjz9WVdEIvamMCcXmuqUYjTknH/sqsWvhQ3vgwKFRR1HpjvNBKQ37nbJgYzGqGcg==} engines: {node: '>=6'} mimic-fn@4.0.0: resolution: {integrity: sha512-vqiC06CuhBTUdZH+RYl8sFrL096vA45Ok5ISO6sE/Mr1jRbGH4Csnhi8f3wKVl7x8mO4Au7Ir9D3Oyv1VYMFJw==} engines: {node: '>=12'} mimic-function@5.0.1: resolution: {integrity: sha512-VP79XUPxV2CigYP3jWwAUFSku2aKqBH7uTAapFWCBqutsbmDo96KY5o8uh6U+/YSIn5OxJnXp73beVkpqMIGhA==} engines: {node: '>=18'} minimatch@10.2.5: resolution: {integrity: sha512-MULkVLfKGYDFYejP07QOurDLLQpcjk7Fw+7jXS2R2czRQzR56yHRveU5NDJEOviH+hETZKSkIk5c+T23GjFUMg==} engines: {node: 18 || 20 || >=22} minimatch@3.1.5: resolution: {integrity: sha512-VgjWUsnnT6n+NUk6eZq77zeFdpW2LWDzP6zFGrCbHXiYNul5Dzqk2HHQ5uFH2DNW5Xbp8+jVzaeNt94ssEEl4w==} minimist@1.2.8: resolution: {integrity: sha512-2yyAR8qBkN3YuheJanUpWC5U3bb5osDywNB8RzDVlDwDHbocAJveqqj1u8+SVD7jkWT4yvsHCpWqqWqAxb0zCA==} mrmime@2.0.1: resolution: {integrity: sha512-Y3wQdFg2Va6etvQ5I82yUhGdsKrcYox6p7FfL1LbK2J4V01F9TGlepTIhnK24t7koZibmg82KGglhA1XK5IsLQ==} engines: {node: '>=10'} ms@2.1.3: resolution: {integrity: sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA==} mz@2.7.0: resolution: {integrity: sha512-z81GNO7nnYMEhrGh9LeymoE4+Yr0Wn5McHIZMK5cfQCl+NDX08sCZgUc9/6MHni9IWuFLm1Z3HTCXu2z9fN62Q==} nanoid@3.3.15: resolution: {integrity: sha512-y7Wygv/7mEOvxTuEQDB8StXdMRBWf1kR/tlhAzBRUFkB2jfcLOAxO/SHmOO2zgz1pVgK29/kyupn059/bCHdjA==} engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true napi-postinstall@0.3.4: resolution: {integrity: sha512-PHI5f1O0EP5xJ9gQmFGMS6IZcrVvTjpXjz7Na41gTE7eE2hK11lg04CECCYEEjdc17EV4DO+fkGEtt7TpTaTiQ==} engines: {node: ^12.20.0 || ^14.18.0 || >=16.0.0} hasBin: true natural-compare@1.4.0: resolution: {integrity: sha512-OWND8ei3VtNC9h7V60qff3SVobHr996CTwgxubgyQYEpg290h9J0buyECNNJexkFm5sOajh5G116RYA1c8ZMSw==} neo-async@2.6.2: resolution: {integrity: sha512-Yd3UES5mWCSqR+qNT93S3UoYUkqAZ9lLg8a7g9rimsWmYGK8cVToA4/sF3RrshdyV3sAGMXVUmpMYOw+dLpOuw==} nerf-dart@1.0.0: resolution: {integrity: sha512-EZSPZB70jiVsivaBLYDCyntd5eH8NTSMOn3rB+HxwdmKThGELLdYv8qVIMWvZEFy9w8ZZpW9h9OB32l1rGtj7g==} node-emoji@2.2.0: resolution: {integrity: sha512-Z3lTE9pLaJF47NyMhd4ww1yFTAP8YhYI8SleJiHzM46Fgpm5cnNzSl9XfzFNqbaz+VlJrIj3fXQ4DeN1Rjm6cw==} engines: {node: '>=18'} node-forge@1.4.0: resolution: {integrity: sha512-LarFH0+6VfriEhqMMcLX2F7SwSXeWwnEAJEsYm5QKWchiVYVvJyV9v7UDvUv+w5HO23ZpQTXDv/GxdDdMyOuoQ==} engines: {node: '>= 6.13.0'} node-releases@2.0.51: resolution: {integrity: sha512-wRNIrw4DmVLKQlbgOMdkMx27Wrpzes2hh5Jtbi2bjPd+4wJstWIqP5A+lscnqbm0xxmT5Bpg8Lec5ItEBwx6BQ==} engines: {node: '>=18'} normalize-package-data@6.0.2: resolution: {integrity: sha512-V6gygoYb/5EmNI+MEGrWkC+e6+Rr7mTmfHrxDbLzxQogBkgzo76rkok0Am6thgSF7Mv2nLOajAJj5vDJZEFn7g==} engines: {node: ^16.14.0 || >=18.0.0} normalize-package-data@8.0.0: resolution: {integrity: sha512-RWk+PI433eESQ7ounYxIp67CYuVsS1uYSonX3kA6ps/3LWfjVQa/ptEg6Y3T6uAMq1mWpX9PQ+qx+QaHpsc7gQ==} engines: {node: ^20.17.0 || >=22.9.0} normalize-url@9.0.1: resolution: {integrity: sha512-ARftfC5HdUNu9jJeL8pHj8debUIHA2b91FizCoMzY4lG6dDX13jdvTK0TBe24IBDRf2HvJSzzwEPvmbkQWHRSg==} engines: {node: '>=20'} npm-normalize-package-bin@6.0.0: resolution: {integrity: sha512-tdt4aFn9QamlhdN3HV2D2ccpBwO5/fyjjbXUxYA6uBjyekMZcZvDq0aSj9t5Jo+tih6AYFnt/cuIRn9013e0Uw==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} npm-run-all2@9.0.2: resolution: {integrity: sha512-+dd4SO2jAlLE06OzmJKzIe6QvvjXezcbmobnh8usR0a8BzQCABTdqTXqVPji0ICOhSQpIIrkGd7IzNl5iDaRSA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0, npm: '>= 10'} hasBin: true npm-run-path@4.0.1: resolution: {integrity: sha512-S48WzZW777zhNIrn7gxOlISNAqi9ZC/uQFnRdbeIHhZhCA6UqpkOT8T1G7BvfdgP4Er8gF4sUbaS0i7QvIfCWw==} engines: {node: '>=8'} npm-run-path@5.3.0: resolution: {integrity: sha512-ppwTtiJZq0O/ai0z7yfudtBpWIoxM8yE6nHi1X47eFR2EWORqfbu6CnPlNsjeN683eT0qG6H/Pyf9fCcvjnnnQ==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} npm-run-path@6.0.0: resolution: {integrity: sha512-9qny7Z9DsQU8Ou39ERsPU4OZQlSTP47ShQzuKZ6PRXpYLtIFgl/DEBYEXKlvcEa+9tHVcK8CF81Y2V72qaZhWA==} engines: {node: '>=18'} npm@11.18.0: resolution: {integrity: sha512-T67M4L5wNm0cZ7EBLErcEkY1SmzEW/WJ+SADBzsFUY1UdAPfFHXFQtZ6SEXiK0+vzXysCvAsepbMaBTwnrAD+w==} engines: {node: ^20.17.0 || >=22.9.0} hasBin: true bundledDependencies: - '@isaacs/string-locale-compare' - '@npmcli/arborist' - '@npmcli/config' - '@npmcli/fs' - '@npmcli/map-workspaces' - '@npmcli/metavuln-calculator' - '@npmcli/package-json' - '@npmcli/promise-spawn' - '@npmcli/redact' - '@npmcli/run-script' - '@sigstore/tuf' - abbrev - archy - cacache - chalk - ci-info - fastest-levenshtein - fs-minipass - glob - graceful-fs - hosted-git-info - ini - init-package-json - is-cidr - json-parse-even-better-errors - libnpmaccess - libnpmdiff - libnpmexec - libnpmfund - libnpmorg - libnpmpack - libnpmpublish - libnpmsearch - libnpmteam - libnpmversion - make-fetch-happen - minimatch - minipass - minipass-pipeline - ms - node-gyp - nopt - npm-audit-report - npm-install-checks - npm-package-arg - npm-pick-manifest - npm-profile - npm-registry-fetch - npm-user-validate - p-map - pacote - parse-conflict-json - proc-log - qrcode-terminal - read - semver - spdx-expression-parse - ssri - supports-color - tar - text-table - tiny-relative-date - treeverse - validate-npm-package-name - which object-assign@4.1.1: resolution: {integrity: sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg==} engines: {node: '>=0.10.0'} object-deep-merge@2.0.1: resolution: {integrity: sha512-aKttDKcU3pyZqKcCkDhsMn70WmZFG2JGDQLP9EcLyTSIFQRCPWLAmBZRLJnrVUrhPG1jETEEbfdgbNtJf1LyMg==} object-inspect@1.13.4: resolution: {integrity: sha512-W67iLl4J2EXEGTbfeHCffrjDfitvLANg0UlX3wFUUSTx92KXRFegMHUVgSqE+wvhAbi4WqjGg9czysTV2Epbew==} engines: {node: '>= 0.4'} object-keys@1.1.1: resolution: {integrity: sha512-NuAESUOUMrlIXOfHKzD6bpPu3tYt3xvjNdRIQ+FeT0lNb4K8WR70CaDxhuNguS2XG+GjkyMwOzsN5ZktImfhLA==} engines: {node: '>= 0.4'} object.assign@4.1.7: resolution: {integrity: sha512-nK28WOo+QIjBkDduTINE4JkF/UJJKyf2EJxvJKfblDpyg0Q+pkOHNTL0Qwy6NP6FhE/EnzV73BxxqcJaXY9anw==} engines: {node: '>= 0.4'} object.fromentries@2.0.8: resolution: {integrity: sha512-k6E21FzySsSK5a21KRADBd/NGneRegFO5pLHfdQLpRDETUNJueLXs3WCzyQ3tFRDYgbq3KHGXfTbi2bs8WQ6rQ==} engines: {node: '>= 0.4'} object.values@1.2.1: resolution: {integrity: sha512-gXah6aZrcUxjWg2zR2MwouP2eHlCBzdV4pygudehaKXSGW4v2AsRQUK+lwwXhii6KFZcunEnmSUoYp5CXibxtA==} engines: {node: '>= 0.4'} obug@2.1.3: resolution: {integrity: sha512-9miFgM2OFba7hB+pRgvtV84pYTBaoTHohvmIgiRt6dRIzbwEOIaNaP+dIlGs2fNFoB0SeISs0Jz5WFVRid6Xyg==} engines: {node: '>=12.20.0'} onetime@5.1.2: resolution: {integrity: sha512-kbpaSSGJTWdAY5KPVeMOKXSrPtr8C8C7wodJbcsd51jRnmD+GZu8Y0VoU6Dm5Z4vWr0Ig/1NKuWRKf7j5aaYSg==} engines: {node: '>=6'} onetime@6.0.0: resolution: {integrity: sha512-1FlR+gjXK7X+AsAHso35MnyN5KqGwJRi/31ft6x0M194ht7S+rWAvd7PHss9xSKMzE0asv1pyIHaJYq+BbacAQ==} engines: {node: '>=12'} onetime@7.0.0: resolution: {integrity: sha512-VXJjc87FScF88uafS3JllDgvAm+c/Slfz06lorj2uAY34rlUu0Nt+v8wreiImcrgAjjIHp1rXpTDlLOGw29WwQ==} engines: {node: '>=18'} optionator@0.9.4: resolution: {integrity: sha512-6IpQ7mKUxRcZNLIObR0hz7lxsapSSIYNZJwXPGeF0mTVqGKFIXj1DQcMoT22S3ROcLyY/rz0PWaWZ9ayWmad9g==} engines: {node: '>= 0.8.0'} own-keys@1.0.1: resolution: {integrity: sha512-qFOyK5PjiWZd+QQIh+1jhdb9LpxTF0qs7Pm8o5QHYZ0M3vKqSqzsZaEB6oWlxZ+q2sJBMI/Ktgd2N5ZwQoRHfg==} engines: {node: '>= 0.4'} oxfmt@0.57.0: resolution: {integrity: sha512-ZB7Bi+rGDSqmVIo9jwcLyFgjxXvQhDdU+jx+ZrVy6VRiVXK2+CHc4hO3J4dUQjHe7V0ymHB+MDuv5z+NhK07HA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: svelte: ^5.0.0 vite-plus: '*' peerDependenciesMeta: svelte: optional: true vite-plus: optional: true oxlint-plugin-eslint@1.73.0: resolution: {integrity: sha512-ZHGk1YDMwxnz2OSxHPysJwgXE7oVIDGWmhlVFeQuvfBDY5AlMfcUlYvixJkjc0sGYNhegYN8we10ZU5UydFLvQ==} engines: {node: ^20.19.0 || >=22.12.0} oxlint-tsgolint@0.24.0: resolution: {integrity: sha512-giCk5sEvG02d5tzPmFMX3hem8ndzEEu1xvGYS5OwNfO2WGl6ZVxt5LjE0yiMDoz94INI7XkXwgFAQiydPvVHDw==} hasBin: true oxlint@1.72.0: resolution: {integrity: sha512-1rhdZIP/EvoI91ABIwNU5Q8+bWf8mjrS5UzIOZld4d4bXxJvtlUhlQvaoTogIGin/qdErMOrwaIJvCSIAKTLhA==} engines: {node: ^20.19.0 || >=22.12.0} hasBin: true peerDependencies: oxlint-tsgolint: '>=0.22.1' vite-plus: '*' peerDependenciesMeta: oxlint-tsgolint: optional: true vite-plus: optional: true p-each-series@3.0.0: resolution: {integrity: sha512-lastgtAdoH9YaLyDa5i5z64q+kzOcQHsQ5SsZJD3q0VEyI8mq872S3geuNbRUQLVAE9siMfgKrpj7MloKFHruw==} engines: {node: '>=12'} p-event@6.0.1: resolution: {integrity: sha512-Q6Bekk5wpzW5qIyUP4gdMEujObYstZl6DMMOSenwBvV0BlE5LkDwkjs5yHbZmdCEq2o4RJx4tE1vwxFVf2FG1w==} engines: {node: '>=16.17'} p-filter@4.1.0: resolution: {integrity: sha512-37/tPdZ3oJwHaS3gNJdenCDB3Tz26i9sjhnguBtvN0vYlRIiDNnvTWkuh+0hETV9rLPdJ3rlL3yVOYPIAnM8rw==} engines: {node: '>=18'} p-limit@1.3.0: resolution: {integrity: sha512-vvcXsLAJ9Dr5rQOPk7toZQZJApBl2K4J6dANSsEuh6QI41JYcsS/qhTGa9ErIUUgK3WNQoJYvylxvjqmiqEA9Q==} engines: {node: '>=4'} p-limit@3.1.0: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} p-locate@5.0.0: resolution: {integrity: sha512-LaNjtRWUBY++zB5nE/NwcaoMylSPk+S+ZHNB1TzdbMJMny6dynpAGt7X/tl/QYq3TIeE6nxHppbo2LGymrG5Pw==} engines: {node: '>=10'} p-map@7.0.5: resolution: {integrity: sha512-e8vJF4XdVkzqqSHguEMz41mQO1wKwxKm5ENrUJQUu9kLDCtn83cxbyHZcszr4QC5zEA7WffRRC4gsTecC7J9oA==} engines: {node: '>=18'} p-reduce@2.1.0: resolution: {integrity: sha512-2USApvnsutq8uoxZBGbbWM0JIYLiEMJ9RlaN7fAzVNb9OZN0SHjjTTfIcb667XynS5Y1VhwDJVDa72TnPzAYWw==} engines: {node: '>=8'} p-reduce@3.0.0: resolution: {integrity: sha512-xsrIUgI0Kn6iyDYm9StOpOeK29XM1aboGji26+QEortiFST1hGZaUQOLhtEbqHErPpGW/aSz6allwK2qcptp0Q==} engines: {node: '>=12'} p-timeout@6.1.4: resolution: {integrity: sha512-MyIV3ZA/PmyBN/ud8vV9XzwTrNtR4jFrObymZYnZqMmW0zA8Z17vnT0rBgFE/TlohB+YCHqXMgZzb3Csp49vqg==} engines: {node: '>=14.16'} p-try@1.0.0: resolution: {integrity: sha512-U1etNYuMJoIz3ZXSrrySFjsXQTWOx2/jdi86L+2pRvph/qMKL6sbcCYdH23fqsbm8TH2Gn0OybpT4eSFlCVHww==} engines: {node: '>=4'} parent-module@1.0.1: resolution: {integrity: sha512-GQ2EWRpQV8/o+Aw8YqtfZZPfNRWZYkbidE9k5rpl/hC3vtHHBfGm2Ifi6qWV+coDGkrUKZAxE3Lot5kcsRlh+g==} engines: {node: '>=6'} parse-entities@4.0.2: resolution: {integrity: sha512-GG2AQYWoLgL877gQIKeRPGO1xF9+eG1ujIb5soS5gPvLQ1y2o8FL90w2QWNdf9I361Mpp7726c+lj3U0qK1uGw==} parse-imports-exports@0.2.4: resolution: {integrity: sha512-4s6vd6dx1AotCx/RCI2m7t7GCh5bDRUtGNvRfHSP2wbBQdMi67pPe7mtzmgwcaQ8VKK/6IB7Glfyu3qdZJPybQ==} parse-json@4.0.0: resolution: {integrity: sha512-aOIos8bujGN93/8Ox/jPLh7RwVnPEysynVFE+fQZyg6jKELEHwzgKdLRFHUgXJL6kylijVSBC4BvN9OmsB48Rw==} engines: {node: '>=4'} parse-json@5.2.0: resolution: {integrity: sha512-ayCKvm/phCGxOkYRSCM82iDwct8/EonSEgCSxWxD7ve6jHggsFl4fZVQBPRNgQoKiuV/odhFrGzQXZwbifC8Rg==} engines: {node: '>=8'} parse-json@8.3.0: resolution: {integrity: sha512-ybiGyvspI+fAoRQbIPRddCcSTV9/LsJbf0e/S85VLowVGzRmokfneg2kwVW/KU5rOXrPSbF1qAKPMgNTqqROQQ==} engines: {node: '>=18'} parse-ms@4.0.0: resolution: {integrity: sha512-TXfryirbmq34y8QBwgqCVLi+8oA3oWx2eAnSn62ITyEhEYaWRlVZ2DvMM9eZbMs/RfxPu/PK/aBLyGj4IrqMHw==} engines: {node: '>=18'} parse-statements@1.0.11: resolution: {integrity: sha512-HlsyYdMBnbPQ9Jr/VgJ1YF4scnldvJpJxCVx6KgqPL4dxppsWrJHCIIxQXMJrqGnsRkNPATbeMJ8Yxu7JMsYcA==} parse5-htmlparser2-tree-adapter@6.0.1: resolution: {integrity: sha512-qPuWvbLgvDGilKc5BoicRovlT4MtYT6JfJyBOMDsKoiT+GiuP5qyrPCnR9HcPECIJJmZh5jRndyNThnhhb/vlA==} parse5@5.1.1: resolution: {integrity: sha512-ugq4DFI0Ptb+WWjAdOK16+u/nHfiIrcE+sh8kZMaM0WllQKLI9rOUq6c2b7cwPkXdzfQESqvoqK6ug7U/Yyzug==} parse5@6.0.1: resolution: {integrity: sha512-Ofn/CTFzRGTTxwpNEs9PP93gXShHcTq255nzRYSKe8AkVpZY7e1fpmTfOyoIvjP5HG7Z2ZM7VS9PPhQGW2pOpw==} path-exists@3.0.0: resolution: {integrity: sha512-bpC7GYwiDYQ4wYLe+FA8lhRjhQCMcQGuSgGGqDkg/QerRWw9CmGRT0iSOVRSZJ29NMLZgIzqaljJ63oaL4NIJQ==} engines: {node: '>=4'} path-exists@4.0.0: resolution: {integrity: sha512-ak9Qy5Q7jYb2Wwcey5Fpvg2KoAc/ZIhLSLOSBmRmygPsGwkVVt0fZa0qrtMz+m6tJTAHfZQ8FnmB4MG4LWy7/w==} engines: {node: '>=8'} path-key@3.1.1: resolution: {integrity: sha512-ojmeN0qd+y0jszEtoY48r0Peq5dwMEkIlCOu6Q5f41lfkswXuKtYrhgoTpLnyIcHm24Uhqx+5Tqm2InSwLhE6Q==} engines: {node: '>=8'} path-key@4.0.0: resolution: {integrity: sha512-haREypq7xkM7ErfgIyA0z+Bj4AGKlMSdlQE2jvJo6huWD1EdkKYV+G/T4nq0YEF2vgTT8kqMFKo1uHn950r4SQ==} engines: {node: '>=12'} path-type@4.0.0: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} pathe@2.0.3: resolution: {integrity: sha512-WUjGcAqP1gQacoQe+OBJsFA7Ld4DyXuUIjZ5cc75cLHvJ7dtNsTugphxIADwspS+AraAUePCKrSVtPLFj/F88w==} picocolors@1.1.1: resolution: {integrity: sha512-xceH2snhtb5M9liqDsmEw56le376mTZkEX/jEb/RxNFyegNul7eNslCXP9FDj/Lcu0X8KEyMceP2ntpaHrDEVA==} picomatch@2.3.2: resolution: {integrity: sha512-V7+vQEJ06Z+c5tSye8S+nHUfI51xoXIXjHQ99cQtKUkQqqO1kO/KCJUfZXuB47h/YBlDhah2H3hdUGXn8ie0oA==} engines: {node: '>=8.6'} picomatch@4.0.5: resolution: {integrity: sha512-RvwwcruNjI1ncT5xRakeyS9Lf8lcItv34KD+aif+VH9kduAyfYBipGh12274xtenIPZ119/R9BdTBa8gAwSh0A==} engines: {node: '>=12'} pidtree@1.0.0: resolution: {integrity: sha512-avfAvjB9Dd0wdj3rjJX//yS+G79OO0KrS5pJHFJENjYGX6N4SMgEDBBI/yFy0lloOYSaC6XQxzpOAMPfSYFV/Q==} engines: {node: '>=18'} hasBin: true pify@3.0.0: resolution: {integrity: sha512-C3FsVNH1udSEX48gGX1xfvwTWfsYWj5U+8/uK15BGzIGrKoUpghX8hWZwa/OFnakBiiVNmBvemTJR5mcy7iPcg==} engines: {node: '>=4'} pinst@3.0.0: resolution: {integrity: sha512-cengSmBxtCyaJqtRSvJorIIZXMXg+lJ3sIljGmtBGUVonMnMsVJbnzl6jGN1HkOWwxNuJynCJ2hXxxqCQrFDdw==} engines: {node: '>=12.0.0'} hasBin: true pkg-conf@2.1.0: resolution: {integrity: sha512-C+VUP+8jis7EsQZIhDYmS5qlNtjv2yP4SNtjXK9AP1ZcTRlnSfuumaTnRfYZnYgUUYVIKqL0fRvmUGDV2fmp6g==} engines: {node: '>=4'} pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} pngjs@7.0.0: resolution: {integrity: sha512-LKWqWJRhstyYo9pGvgor/ivk2w94eSjE3RGVuzLGlr3NmD8bf7RcYGze1mNdEHRP6TRP6rMuDHk5t44hnTRyow==} engines: {node: '>=14.19.0'} possible-typed-array-names@1.1.0: resolution: {integrity: sha512-/+5VFTchJDoVj3bhoqi6UeymcD00DAwb1nJwamzPvHEszJ4FpF6SNNbUbOS8yI56qHzdV8eK0qEfOSiodkTdxg==} engines: {node: '>= 0.4'} postcss@8.5.16: resolution: {integrity: sha512-vuwillviilfKZsg0VGj5R/YwwcHx4SLsIOI/7K6mQkWx+l5cUHTjj5g0AasTBcyXsbfTgrwsUNmVUb5xVwyPwg==} engines: {node: ^10 || ^12 || >=14} prelude-ls@1.2.1: resolution: {integrity: sha512-vkcDPrRZo1QZLbn5RLGPpg/WmIQ65qoWWhcGKf/b5eplkkarX0m9z8ppCat4mlOqUsWpyNuYgO3VRyrYHSzX5g==} engines: {node: '>= 0.8.0'} prettier-linter-helpers@1.0.1: resolution: {integrity: sha512-SxToR7P8Y2lWmv/kTzVLC1t/GDI2WGjMwNhLLE9qtH8Q13C+aEmuRlzDst4Up4s0Wc8sF2M+J57iB3cMLqftfg==} engines: {node: '>=6.0.0'} prettier@3.9.5: resolution: {integrity: sha512-/FVl766LpUfB5vXgCYOYa0MeV/441Ia99AeICQIQFTY/Nw0roZwULcXpku5i1/m5kt/baz+s4Zogspd839HSMg==} engines: {node: '>=14'} hasBin: true pretty-format@27.5.1: resolution: {integrity: sha512-Qb1gy5OrP5+zDf2Bvnzdl3jsTf1qXVMazbvCoKhtKqVs4/YK4ozX4gKQJJVyNe+cajNPn0KoC0MC3FUmaHWEmQ==} engines: {node: ^10.13.0 || ^12.13.0 || ^14.15.0 || >=15.0.0} pretty-ms@9.3.0: resolution: {integrity: sha512-gjVS5hOP+M3wMm5nmNOucbIrqudzs9v/57bWRHQWLYklXqoXKrVfYW2W9+glfGsqtPgpiz5WwyEEB+ksXIx3gQ==} engines: {node: '>=18'} process-nextick-args@2.0.1: resolution: {integrity: sha512-3ouUOpQhtgrbOa17J7+uxOTpITYWaGP7/AhoR3+A+/1e9skrzelGi/dXzEYyvbxubEF6Wn2ypscTKiKJFFn1ag==} proto-list@1.2.4: resolution: {integrity: sha512-vtK/94akxsTMhe0/cbfpR+syPuszcuwhqVjJq26CuNDgFGj682oRBXOP5MJpv2r7JtE8MsiepGIqvvOTBwn2vA==} proxy-agent-negotiate@1.1.0: resolution: {integrity: sha512-N8IBcM3UgCVzz2L2Lqv8DVntDnnC8/hiV4nEDUPkqq72TPUgYWjQc+bdZlBPZK9LzPAvOY//gAt0S0DApoOXWQ==} engines: {node: '>= 20'} peerDependencies: kerberos: ^2.0.0 peerDependenciesMeta: kerberos: optional: true punycode.js@2.3.1: resolution: {integrity: sha512-uxFIHU0YlHYhDQtV4R9J6a52SLx28BCjT+4ieh7IGbgwVJWO+km431c4yRlREUAsAmt/uMjQUyQHNEPf0M39CA==} engines: {node: '>=6'} punycode@2.3.1: resolution: {integrity: sha512-vYt7UD1U9Wg6138shLtLOvdAu+8DsC/ilFtEVHcH+wydcSpNE20AfSOduf6MkRFahL5FY7X1oU7nKVZFtfq8Fg==} engines: {node: '>=6'} queue-microtask@1.2.3: resolution: {integrity: sha512-NuaNSa6flKT5JaSYQzJok04JzTL1CA6aGhv5rfLW3PgqA+M2ChpZQnAC8h8i4ZFkBS8X5RqkDBHA7r4hej3K9A==} rc@1.2.8: resolution: {integrity: sha512-y3bGgqKj3QBdxLbLkomlohkvsA8gdAiUQlSBJnBhfn+BPxg4bc62d8TcBW15wavDfgexCgccckhcZvywyQYPOw==} hasBin: true react-is@17.0.2: resolution: {integrity: sha512-w2GsyukL62IJnlaff/nRegPQR94C/XXamvMWmSHRJ4y7Ts/4ocGRmTHvOs8PSE6pB3dWOrD/nueuU5sduBsQ4w==} read-package-json-fast@6.0.0: resolution: {integrity: sha512-PNaGjoCnw9DBA2Kl8D+8po957z778q/HOPuY2u3Bkw/JO3eC8MDx7jn/PgMtSgpcBbs+6UOjDbwReGpXmRvs0g==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} read-package-up@11.0.0: resolution: {integrity: sha512-MbgfoNPANMdb4oRBNg5eqLbB2t2r+o5Ua1pNt8BqGp4I0FJZhuVSOj3PaBPni4azWuSzEdNn2evevzVmEk1ohQ==} engines: {node: '>=18'} read-package-up@12.0.0: resolution: {integrity: sha512-Q5hMVBYur/eQNWDdbF4/Wqqr9Bjvtrw2kjGxxBbKLbx8bVCL8gcArjTy8zDUuLGQicftpMuU0riQNcAsbtOVsw==} engines: {node: '>=20'} read-pkg@10.1.0: resolution: {integrity: sha512-I8g2lArQiP78ll51UeMZojewtYgIRCKCWqZEgOO8c/uefTI+XDXvCSXu3+YNUaTNvZzobrL5+SqHjBrByRRTdg==} engines: {node: '>=20'} read-pkg@9.0.1: resolution: {integrity: sha512-9viLL4/n1BJUCT1NXVTdS1jtm80yDEgR5T4yCelII49Mbj0v1rZdKqj7zCiYdbB0CuCgdrvHcNogAKTFPBocFA==} engines: {node: '>=18'} readable-stream@2.3.8: resolution: {integrity: sha512-8p0AUk4XODgIewSi0l8Epjs+EVnWiK7NoDIEGU0HhE7+ZyY8D1IMY7odu5lRrFXGg71L15KG8QrPmum45RTtdA==} reflect.getprototypeof@1.0.10: resolution: {integrity: sha512-00o4I+DVrefhv+nX0ulyi3biSHCPDe+yLv5o/p6d/UVlirijB8E16FtfwSAi4g3tcqrQ4lRAqQSoFEZJehYEcw==} engines: {node: '>= 0.4'} regexp-tree@0.1.27: resolution: {integrity: sha512-iETxpjK6YoRWJG5o6hXLwvjYAoW+FEZn9os0PD/b6AP6xQwsa/Y7lCVgIixBbUPMfhu+i2LtdeAqVTgGlQarfA==} hasBin: true regexp.prototype.flags@1.5.4: resolution: {integrity: sha512-dYqgNSZbDwkaJ2ceRd9ojCGjBq+mOm9LmtXnAnEGyHhN/5R7iDW2TRw3h+o/jCFxus3P2LfWIIiwowAjANm7IA==} engines: {node: '>= 0.4'} registry-auth-token@5.1.1: resolution: {integrity: sha512-P7B4+jq8DeD2nMsAcdfaqHbssgHtZ7Z5+++a5ask90fvmJ8p5je4mOa+wzu+DB4vQ5tdJV/xywY+UnVFeQLV5Q==} engines: {node: '>=14'} regjsparser@0.13.2: resolution: {integrity: sha512-NgRBy2Nx/bE+9F27nVHnqcN5HjyLmecqsqx2PJHu3/IEtADD4WuxuXIVExD5PoSDFVrl78dOonfcOe5O+5nbzQ==} hasBin: true require-directory@2.1.1: resolution: {integrity: sha512-fGxEI7+wsG9xrvdjsrlmL22OMTTiHRwAMroiEeMgq8gzoLC/PQr7RsRDSTLUg/bZAZtF+TVIkHc6/4RIKrui+Q==} engines: {node: '>=0.10.0'} reserved-identifiers@1.2.0: resolution: {integrity: sha512-yE7KUfFvaBFzGPs5H3Ops1RevfUEsDc5Iz65rOwWg4lE8HJSYtle77uul3+573457oHvBKuHYDl/xqUkKpEEdw==} engines: {node: '>=18'} resolve-from@4.0.0: resolution: {integrity: sha512-pb/MYmXstAkysRFx8piNI1tGFNQIFA3vkE3Gq4EuA1dF6gHp/+vgZqsCGJapvy8N3Q+4o7FwvquPJcnZ7RYy4g==} engines: {node: '>=4'} resolve-from@5.0.0: resolution: {integrity: sha512-qYg9KP24dD5qka9J47d0aVky0N+b4fTU89LN9iDnjB5waksiC49rvMB0PrUJQGoTmH50XPiqOvAjDfaijGxYZw==} engines: {node: '>=8'} resolve-pkg-maps@1.0.0: resolution: {integrity: sha512-seS2Tj26TBVOC2NIc2rOe2y2ZO7efxITtLZcGSOnHHNOQ7CkiUBfw0Iw2ck6xkIhPwLhKNLS8BO+hEpngQlqzw==} restore-cursor@5.1.0: resolution: {integrity: sha512-oMA2dcrw6u0YfxJQXm342bFKX/E4sG9rbTzO9ptUcR/e8A33cHuvStiYOwH7fszkZlZ1z/ta9AAoPk2F4qIOHA==} engines: {node: '>=18'} reusify@1.1.0: resolution: {integrity: sha512-g6QUff04oZpHs0eG5p83rFLhHeV00ug/Yf9nZM6fLeUrPguBTkTQOdpAWWspMh55TZfVQDPaN3NQJfbVRAxdIw==} engines: {iojs: '>=1.0.0', node: '>=0.10.0'} rfdc@1.4.1: resolution: {integrity: sha512-q1b3N5QkRUWUl7iyylaaj3kOpIT0N2i9MqIEQXP73GVsN9cw3fdx8X63cEmWhJGi2PPCF23Ijp7ktmd39rawIA==} run-con@1.3.2: resolution: {integrity: sha512-CcfE+mYiTcKEzg0IqS08+efdnH0oJ3zV0wSUFBNrMHMuxCtXvBCLzCJHatwuXDcu/RlhjTziTo/a1ruQik6/Yg==} hasBin: true run-parallel@1.2.0: resolution: {integrity: sha512-5l4VyZR86LZ/lDxZTR6jqL8AFE2S0IFLMP26AbjsLVADxHdhB/c0GUsH+y39UfCi3dzz8OlQuPmnaJOMoDHQBA==} safe-array-concat@1.1.4: resolution: {integrity: sha512-wtZlHyOje6OZTGqAoaDKxFkgRtkF9CnHAVnCHKfuj200wAgL+bSJhdsCD2l0Qx/2ekEXjPWcyKkfGb5CPboslg==} engines: {node: '>=0.4'} safe-buffer@5.1.2: resolution: {integrity: sha512-Gd2UZBJDkXlY7GbJxfsE8/nvKkUEU1G38c1siN6QP6a9PT9MmHB8GnpscSmMJSoF8LOIrt8ud/wPtojys4G6+g==} safe-push-apply@1.0.0: resolution: {integrity: sha512-iKE9w/Z7xCzUMIZqdBsp6pEQvwuEebH4vdpjcDWnyzaI6yl6O9FHvVpmGelvEHNsoY6wGblkxR6Zty/h00WiSA==} engines: {node: '>= 0.4'} safe-regex-test@1.1.0: resolution: {integrity: sha512-x/+Cz4YrimQxQccJf5mKEbIa1NzeCRNI5Ecl/ekmlYaampdNLPalVyIcCZNNH3MvmqBugV5TMYZXv0ljslUlaw==} engines: {node: '>= 0.4'} safe-regex@2.1.1: resolution: {integrity: sha512-rx+x8AMzKb5Q5lQ95Zoi6ZbJqwCLkqi3XuJXp5P3rT8OEc6sZCJG5AE5dU3lsgRr/F4Bs31jSlVN+j5KrsGu9A==} semantic-release@25.0.5: resolution: {integrity: sha512-mn61SUJwtM8ThrWn2WmgLVpwVJeG/hPSupua1psdMoufmwRIPyvRLkRkL0JDXkP67OntlLWUYnBnfVc8EDO3/g==} engines: {node: ^22.14.0 || >= 24.10.0} hasBin: true semver-regex@4.0.5: resolution: {integrity: sha512-hunMQrEy1T6Jr2uEVjrAIqjwWcQTgOAcIM52C8MY1EZSD3DDNft04XzvYKPqjED65bNVVko0YI38nYeEHCX3yw==} engines: {node: '>=12'} semver@6.3.1: resolution: {integrity: sha512-BR7VvDCVHO+q2xBEWskxS6DJE1qRnb7DxzUrogb71CWoSficBxYsiAGd+Kl0mmq/MprG9yArRkyrQxTO6XjMzA==} hasBin: true semver@7.8.5: resolution: {integrity: sha512-Y7/KDsb8LjooZpwaqGyulO6DQlksgCncchHGk+sZIY4SBvUocMBEFH5Ur1fI4dV+Jvl0w6cjvucaIi40puRioA==} engines: {node: '>=10'} hasBin: true set-function-length@1.2.2: resolution: {integrity: sha512-pgRc4hJ4/sNjWCSS9AmnS40x3bNMDTknHgL5UaMBTMyJnU90EgWh1Rz+MC9eFu4BuN/UwZjKQuY/1v3rM7HMfg==} engines: {node: '>= 0.4'} set-function-name@2.0.2: resolution: {integrity: sha512-7PGFlmtwsEADb0WYyvCMa1t+yke6daIG4Wirafur5kcf+MhUnPms1UeR0CKQdTZD81yESwMHbtn+TR+dMviakQ==} engines: {node: '>= 0.4'} set-proto@1.0.0: resolution: {integrity: sha512-RJRdvCo6IAnPdsvP/7m6bsQqNnn1FCBX5ZNtFL98MmFF/4xAIJTIg1YbHW5DC2W5SKZanrC6i4HsJqlajw/dZw==} engines: {node: '>= 0.4'} shebang-command@2.0.0: resolution: {integrity: sha512-kHxr2zZpYtdmrN1qDjrrX/Z1rR1kG8Dx+gkpK1G4eXmvXswmcE1hTWBWYUzlraYw1/yZp6YuDY77YtvbN0dmDA==} engines: {node: '>=8'} shebang-regex@3.0.0: resolution: {integrity: sha512-7++dFhtcx3353uBaq8DDR4NuxBetBzC7ZQOhmTQInHEd6bSrXdiEyzCvG07Z44UYdLShWUyXt5M/yhz8ekcb1A==} engines: {node: '>=8'} shell-quote@1.9.0: resolution: {integrity: sha512-Iov+JwFv/2HcTpcwNMKd8+IWNb8tboQJNQTkAY/LLVK7gGH9jy+LGkVqPxfekHl+yMmiqXszdGWXgkfml7hjqA==} engines: {node: '>= 0.4'} side-channel-list@1.0.1: resolution: {integrity: sha512-mjn/0bi/oUURjc5Xl7IaWi/OJJJumuoJFQJfDDyO46+hBWsfaVM65TBHq2eoZBhzl9EchxOijpkbRC8SVBQU0w==} engines: {node: '>= 0.4'} side-channel-map@1.0.1: resolution: {integrity: sha512-VCjCNfgMsby3tTdo02nbjtM/ewra6jPHmpThenkTYh8pG9ucZ/1P8So4u4FGBek/BjpOVsDCMoLA/iuBKIFXRA==} engines: {node: '>= 0.4'} side-channel-weakmap@1.0.2: resolution: {integrity: sha512-WPS/HvHQTYnHisLo9McqBHOJk2FkHO/tlpvldyrnem4aeQp4hai3gythswg6p01oSoTl58rcpiFAjF2br2Ak2A==} engines: {node: '>= 0.4'} side-channel@1.1.1: resolution: {integrity: sha512-6x6dK6zJdpTzF4sQeNYxwtvBzf6Eg4GtlesS94HOvTudUeyK2WXAaIfmDgsyslYrRBeFIlsi54AYsFGUuhmvrQ==} engines: {node: '>= 0.4'} siginfo@2.0.0: resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} signal-exit@4.1.0: resolution: {integrity: sha512-bzyZ1e88w9O1iNJbKnOlvYTrWPDl46O1bG0D3XInv+9tkPrxrN8jUUTiFlDkkmKWgn1M6CfIA13SuGqOa9Korw==} engines: {node: '>=14'} signale@1.4.0: resolution: {integrity: sha512-iuh+gPf28RkltuJC7W5MRi6XAjTDCAPC/prJUpQoG4vIP3MJZ+GTydVnodXA7pwvTKb2cA0m9OFZW/cdWy/I/w==} engines: {node: '>=6'} sirv@3.0.2: resolution: {integrity: sha512-2wcC/oGxHis/BoHkkPwldgiPSYcpZK3JU28WoMVv55yHJgcZ8rlXvuG9iZggz+sU1d4bRgIGASwyWqjxu3FM0g==} engines: {node: '>=18'} skin-tone@2.0.0: resolution: {integrity: sha512-kUMbT1oBJCpgrnKoSr0o6wPtvRWT9W9UKvGLwfJYO2WuahZRHOpEyL1ckyMGgMWh0UdpmaoFqKKD29WTomNEGA==} engines: {node: '>=8'} slice-ansi@7.1.2: resolution: {integrity: sha512-iOBWFgUX7caIZiuutICxVgX1SdxwAVFFKwt1EvMYYec/NWO5meOJ6K5uQxhrYBdQJne4KxiqZc+KptFOWFSI9w==} engines: {node: '>=18'} slice-ansi@8.0.0: resolution: {integrity: sha512-stxByr12oeeOyY2BlviTNQlYV5xOj47GirPr4yA1hE9JCtxfQN0+tVbkxwCtYDQWhEKWFHsEK48ORg5jrouCAg==} engines: {node: '>=20'} smol-toml@1.6.1: resolution: {integrity: sha512-dWUG8F5sIIARXih1DTaQAX4SsiTXhInKf1buxdY9DIg4ZYPZK5nGM1VRIYmEbDbsHt7USo99xSLFu5Q1IqTmsg==} engines: {node: '>= 18'} source-map-js@1.2.1: resolution: {integrity: sha512-UXWMKhLOwVKb728IUtQPXxfYU+usdybtUrK/8uGE8CQMvrhOpwvzDBwj0QhSL7MQc7vIsISBG8VQ8+IDQxpfQA==} engines: {node: '>=0.10.0'} source-map@0.6.1: resolution: {integrity: sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==} engines: {node: '>=0.10.0'} spawn-error-forwarder@1.0.0: resolution: {integrity: sha512-gRjMgK5uFjbCvdibeGJuy3I5OYz6VLoVdsOJdA6wV0WlfQVLFueoqMxwwYD9RODdgb6oUIvlRlsyFSiQkMKu0g==} spdx-correct@3.2.0: resolution: {integrity: sha512-kN9dJbvnySHULIluDHy32WHRUu3Og7B9sbY7tsFLctQkIqnMh3hErYgdMjTYuqmcXX+lK5T1lnUt3G7zNswmZA==} spdx-exceptions@2.5.0: resolution: {integrity: sha512-PiU42r+xO4UbUS1buo3LPJkjlO7430Xn5SVAhdpzzsPHsjbYVflnnFdATgabnLude+Cqu25p6N+g2lw/PFsa4w==} spdx-expression-parse@3.0.1: resolution: {integrity: sha512-cbqHunsQWnJNE6KhVSMsMeH5H/L9EpymbzqTQ3uLwNCLZ1Q481oWaofqH7nO6V07xlXwY6PhQdQ2IedWx/ZK4Q==} spdx-expression-parse@4.0.0: resolution: {integrity: sha512-Clya5JIij/7C6bRR22+tnGXbc4VKlibKSVj2iHvVeX5iMW7s1SIQlqu699JkODJJIhh/pUu8L0/VLh8xflD+LQ==} spdx-license-ids@3.0.23: resolution: {integrity: sha512-CWLcCCH7VLu13TgOH+r8p1O/Znwhqv/dbb6lqWy67G+pT1kHmeD/+V36AVb/vq8QMIQwVShJ6Ssl5FPh0fuSdw==} split2@1.0.0: resolution: {integrity: sha512-NKywug4u4pX/AZBB1FCPzZ6/7O+Xhz1qMVbzTvvKvikjO99oPN87SkK08mEY9P63/5lWjK+wgOOgApnTg5r6qg==} stable-hash-x@0.2.0: resolution: {integrity: sha512-o3yWv49B/o4QZk5ZcsALc6t0+eCelPc44zZsLtCQnZPDwFpDYSWcDnrv2TtMmMbQ7uKo3J0HTURCqckw23czNQ==} engines: {node: '>=12.0.0'} stackback@0.0.2: resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} std-env@4.2.0: resolution: {integrity: sha512-oCUKSupKTHX53EyjDtuZQ64pjLJ6yYCtpmEw0goYxtjG9KpbRe8KAsl2tBUGU9DyMcJ0RwJ8GqJAFzMXcXW1Rw==} stop-iteration-iterator@1.1.0: resolution: {integrity: sha512-eLoXW/DHyl62zxY4SCaIgnRhuMr6ri4juEYARS8E6sCEqzKpOiE521Ucofdx+KnDZl5xmvGYaaKCk5FEOxJCoQ==} engines: {node: '>= 0.4'} stream-combiner2@1.1.1: resolution: {integrity: sha512-3PnJbYgS56AeWgtKF5jtJRT6uFJe56Z0Hc5Ngg/6sI6rIt8iiMBTa9cvdyFfpMQjaVHr8dusbNeFGIIonxOvKw==} strict-event-emitter-types@2.0.0: resolution: {integrity: sha512-Nk/brWYpD85WlOgzw5h173aci0Teyv8YdIAEtV+N88nDB0dLlazZyJMIsN6eo1/AR61l+p6CJTG1JIyFaoNEEA==} string-argv@0.3.2: resolution: {integrity: sha512-aqD2Q0144Z+/RqG52NeHEkZauTAUWJO8c6yTftGJKO3Tja5tUgIfmIl6kExvhtxSDP7fXB6DvzkfMpCd/F3G+Q==} engines: {node: '>=0.6.19'} string-width@4.2.3: resolution: {integrity: sha512-wKyQRQpjJ0sIp62ErSZdGsjMJWsap5oRNihHhu6G7JVO/9jIB6UyevL+tXuOqrng8j/cxKTWyWUwvSTriiZz/g==} engines: {node: '>=8'} string-width@7.2.0: resolution: {integrity: sha512-tsaTIkKW9b4N+AEj+SVA+WhJzV7/zMhcSu78mLKWSk7cXMOSHsBKFWUs0fWwq8QyK3MgJBQRX6Gbi4kYbdvGkQ==} engines: {node: '>=18'} string-width@8.2.1: resolution: {integrity: sha512-IIaP0g3iy9Cyy18w3M9YcaDudujEAVHKt3a3QJg1+sr/oX96TbaGUubG0hJyCjCBThFH+tFpcIyoUHUn1ogaLA==} engines: {node: '>=20'} string-width@8.2.2: resolution: {integrity: sha512-GaPUh5gfdrYzqeVNZvUfT23vYYxXzKYidUcnMtJg/3rxRV63EFZy3k6xfKlmfeJD0176lnUV/Usr3XcwSvFzpg==} engines: {node: '>=20'} string.prototype.includes@2.0.1: resolution: {integrity: sha512-o7+c9bW6zpAdJHTtujeePODAhkuicdAryFsfVKwA+wGw89wJ4GTY484WTucM9hLtDEOpOvI+aHnzqnC5lHp4Rg==} engines: {node: '>= 0.4'} string.prototype.trim@1.2.11: resolution: {integrity: sha512-PwvK7BU+CMTJGYQCTZb5RWXIML92lftJLhQz1tBzgKiqGxJaMlBAa48POXaNAC2s4y8jr3EFqrkF9+44neS46w==} engines: {node: '>= 0.4'} string.prototype.trimend@1.0.10: resolution: {integrity: sha512-2+3aDAOmPTmuFwjDnmJG2ctEkQKVki7vOSqaxkv42Mowj1V6PnvuwFCRrR5lChUux1TBskPjfkeTOhqczDMxTw==} engines: {node: '>= 0.4'} string.prototype.trimstart@1.0.8: resolution: {integrity: sha512-UXSH262CSZY1tfu3G3Secr6uGLCFVPMhIqHjlgCUtCCcgihYc/xKs9djMTMUOb2j1mVSeU8EU6NWc/iQKU6Gfg==} engines: {node: '>= 0.4'} string_decoder@1.1.1: resolution: {integrity: sha512-n/ShnvDi6FHbbVfviro+WojiFzv+s8MPMHBczVePfUpDJLwoLT0ht1l4YwBCbi8pJAveEEdnkHyPyTP/mzRfwg==} strip-ansi@6.0.1: resolution: {integrity: sha512-Y38VPSHcqkFrCpFnQ9vuSXmquuv5oXOKpGeT6aGrr3o3Gc9AlVa6JBfUSOCnbxGGZF+/0ooI7KrPuUSztUdU5A==} engines: {node: '>=8'} strip-ansi@7.2.0: resolution: {integrity: sha512-yDPMNjp4WyfYBkHnjIRLfca1i6KMyGCtsVgoKe/z1+6vukgaENdgGBZt+ZmKPc4gavvEZ5OgHfHdrazhgNyG7w==} engines: {node: '>=12'} strip-bom@3.0.0: resolution: {integrity: sha512-vavAMRXOgBVNF6nyEEmL3DBK19iRpDcoIwW+swQ+CbGiu7lju6t+JklA1MHweoWtadgt4ISVUsXLyDq34ddcwA==} engines: {node: '>=4'} strip-final-newline@2.0.0: resolution: {integrity: sha512-BrpvfNAE3dcvq7ll3xVumzjKjZQ5tI1sEUIKr3Uoks0XUl45St3FlatVqef9prk4jRDzhW6WZg+3bk93y6pLjA==} engines: {node: '>=6'} strip-final-newline@3.0.0: resolution: {integrity: sha512-dOESqjYr96iWYylGObzd39EuNTa5VJxyvVAEm5Jnh7KGo75V43Hk1odPQkNDyXNmUR6k+gEiDVXnjB8HJ3crXw==} engines: {node: '>=12'} strip-final-newline@4.0.0: resolution: {integrity: sha512-aulFJcD6YK8V1G7iRB5tigAP4TsHBZZrOV8pjV++zdUwmeV8uzbY7yn6h9MswN62adStNZFuCIx4haBnRuMDaw==} engines: {node: '>=18'} strip-indent@4.1.1: resolution: {integrity: sha512-SlyRoSkdh1dYP0PzclLE7r0M9sgbFKKMFXpFRUMNuKhQSbC6VQIGzq3E0qsfvGJaUFJPGv6Ws1NZ/haTAjfbMA==} engines: {node: '>=12'} strip-json-comments@2.0.1: resolution: {integrity: sha512-4gB8na07fecVVkOI6Rs4e7T6NOTki5EmL7TUduTs6bu3EdnSycntVJ4re8kgZA+wx9IueI2Y11bfbgwtzuE0KQ==} engines: {node: '>=0.10.0'} strip-json-comments@3.1.1: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} super-regex@1.1.0: resolution: {integrity: sha512-WHkws2ZflZe41zj6AolvvmaTrWds/VuyeYr9iPVv/oQeaIoVxMKaushfFWpOGDT+GuBrM/sVqF8KUCYQlSSTdQ==} engines: {node: '>=18'} supports-color@5.5.0: resolution: {integrity: sha512-QjVjwdXIt408MIiAqCX4oUKsgU2EqAGzs2Ppkm4aQYbjm+ZEWEcW4SfFNTr4uMNZma0ey4f5lgLrkB0aX0QMow==} engines: {node: '>=4'} supports-color@7.2.0: resolution: {integrity: sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==} engines: {node: '>=8'} supports-hyperlinks@3.2.0: resolution: {integrity: sha512-zFObLMyZeEwzAoKCyu1B91U79K2t7ApXuQfo8OuxwXLDgcKxuwM+YvcbIhm6QWqz7mHUH1TVytR1PwVVjEuMig==} engines: {node: '>=14.18'} synckit@0.11.13: resolution: {integrity: sha512-eNRKgb3z66Yp3D2CixVujOUvXLFUTij/zVnV8KRyvFdQwpz7I5DS8UfRkTeLzb64u+dkzDSdelE24izu+zSSUg==} engines: {node: ^14.18.0 || >=16.0.0} tagged-tag@1.0.0: resolution: {integrity: sha512-yEFYrVhod+hdNyx7g5Bnkkb0G6si8HJurOoOEgC8B/O0uXLHlaey/65KRv6cuWBNhBgHKAROVpc7QyYqE5gFng==} engines: {node: '>=20'} temp-dir@3.0.0: resolution: {integrity: sha512-nHc6S/bwIilKHNRgK/3jlhDoIHcp45YgyiwcAk46Tr0LfEqGBVpmiAyuiuxeVE44m3mXnEeVhaipLOEWmH+Njw==} engines: {node: '>=14.16'} tempy@3.2.0: resolution: {integrity: sha512-d79HhZya5Djd7am0q+W4RTsSU+D/aJzM+4Y4AGJGuGlgM2L6sx5ZvOYTmZjqPhrDrV6xJTtRSm1JCLj6V6LHLQ==} engines: {node: '>=14.16'} thenify-all@1.6.0: resolution: {integrity: sha512-RNxQH/qI8/t3thXJDwcstUO4zeqo64+Uy/+sNVRBx4Xn2OX+OZ9oP+iJnNFqplFra2ZUVeKCSa2oVWi3T4uVmA==} engines: {node: '>=0.8'} thenify@3.3.1: resolution: {integrity: sha512-RVZSIV5IG10Hk3enotrhvz0T9em6cyHBLkH/YAZuKqd8hRkKhSfCGIcP2KUY0EPxndzANBmNllzWPwak+bheSw==} through2@2.0.5: resolution: {integrity: sha512-/mrRod8xqpA+IHSLyGCQ2s8SPHiCDEeQJSep1jqLYeEUClOFG2Qsh+4FU6G9VeqpZnGW/Su8LQGc4YKni5rYSQ==} time-span@5.1.0: resolution: {integrity: sha512-75voc/9G4rDIJleOo4jPvN4/YC4GRZrY8yy1uU4lwrB3XEQbWve8zXoO5No4eFrGcTAMYyoY67p8jRQdtA1HbA==} engines: {node: '>=12'} tinybench@2.9.0: resolution: {integrity: sha512-0+DUvqWMValLmha6lr4kD8iAMK1HzV0/aKnCtWb9v9641TnP/MFb7Pc2bxoxQjTXAErryXVgUOfv2YqNllqGeg==} tinyexec@1.2.4: resolution: {integrity: sha512-SHf/r48b7vOrjve9PxJo3MN5v5yuyjHvdUcrQffT3WXMUfnGmHDVbC4k3sHJaJTgZCwpUplIaAo5ANtMyp3YHg==} engines: {node: '>=18'} tinyglobby@0.2.17: resolution: {integrity: sha512-wXR/dYpcqKmfWpEdZjiKJOwCNFndD0DMnrW/cYjVGttEkBfVgcLFHoNrlj47mjOVic9yyNu65alsgF4NQyTa2g==} engines: {node: '>=12.0.0'} tinypool@2.1.0: resolution: {integrity: sha512-Pugqs6M0m7Lv1I7FtxN4aoyToKg1C4tu+/381vH35y8oENM/Ai7f7C4StcoK4/+BSw9ebcS8jRiVrORFKCALLw==} engines: {node: ^20.0.0 || >=22.0.0} tinyrainbow@3.1.0: resolution: {integrity: sha512-Bf+ILmBgretUrdJxzXM0SgXLZ3XfiaUuOj/IKQHuTXip+05Xn+uyEYdVg0kYDipTBcLrCVyUzAPz7QmArb0mmw==} engines: {node: '>=14.0.0'} to-regex-range@5.0.1: resolution: {integrity: sha512-65P7iz6X5yEr1cwcgvQxbbIw7Uk3gOy5dIdtZ4rDveLqhrdJP+Li/Hx6tyK0NEb+2GCyneCMJiGqrADCSNk8sQ==} engines: {node: '>=8.0'} to-valid-identifier@1.0.0: resolution: {integrity: sha512-41wJyvKep3yT2tyPqX/4blcfybknGB4D+oETKLs7Q76UiPqRpUJK3hr1nxelyYO0PHKVzJwlu0aCeEAsGI6rpw==} engines: {node: '>=20'} totalist@3.0.1: resolution: {integrity: sha512-sf4i37nQ2LBx4m3wB74y+ubopq6W/dIzXg0FDGjsYnZHVa1Da8FH853wlL2gtUhg+xJXjfk3kUZS3BRoQeoQBQ==} engines: {node: '>=6'} traverse@0.6.8: resolution: {integrity: sha512-aXJDbk6SnumuaZSANd21XAo15ucCDE38H4fkqiGsc3MhCK+wOlZvLP9cB/TvpHT0mOyWgC4Z8EwRlzqYSUzdsA==} engines: {node: '>= 0.4'} ts-api-utils@2.5.0: resolution: {integrity: sha512-OJ/ibxhPlqrMM0UiNHJ/0CKQkoKF243/AEmplt3qpRgkW8VG7IfOS41h7V8TjITqdByHzrjcS/2si+y4lIh8NA==} engines: {node: '>=18.12'} peerDependencies: typescript: '>=4.8.4' tslib@2.8.1: resolution: {integrity: sha512-oJFu94HQb+KVduSUQL7wnpmqnfmLsOA/nAh6b6EH0wCEoK0/mPeXU6c3wKDV83MkOuHPRHtSXKKU99IBazS/2w==} tunnel@0.0.6: resolution: {integrity: sha512-1h/Lnq9yajKY2PEbBadPXj3VxsDDu844OnaAo52UVmIzIvwwtBPIuNvkjuzBlTWpfJyUbG3ez0KSBibQkj4ojg==} engines: {node: '>=0.6.11 <=0.7.0 || >=0.7.3'} type-check@0.4.0: resolution: {integrity: sha512-XleUoc9uwGXqjWwXaUTZAmzMcFZ5858QA2vvx1Ur5xIcixXIP+8LnFDgRplU30us6teqdlskFfu+ae4K79Ooew==} engines: {node: '>= 0.8.0'} type-fest@1.4.0: resolution: {integrity: sha512-yGSza74xk0UG8k+pLh5oeoYirvIiWo5t0/o3zHHAO2tRDiZcxWP7fywNlXhqb6/r6sWvwi+RsyQMWhVLe4BVuA==} engines: {node: '>=10'} type-fest@2.19.0: resolution: {integrity: sha512-RAH822pAdBgcNMAfWnCBU3CFZcfZ/i1eZjwFU/dsLKumyuuP3niueg2UAukXYF0E2AAoc82ZSSf9J0WQBinzHA==} engines: {node: '>=12.20'} type-fest@4.41.0: resolution: {integrity: sha512-TeTSQ6H5YHvpqVwBRcnLDCBnDOHWYu7IvGbHT6N8AOymcr9PJGjc1GTtiWZTYg0NCgYwvnYWEkVChQAr9bjfwA==} engines: {node: '>=16'} type-fest@5.8.0: resolution: {integrity: sha512-YGYEVz3Fm5iy/AybuA0oyNFq7H4CgQNfRp/qfe8nurE1kuCeNm3/vfm9X4Mtl+qLyaKJUh5xrFZwogr41SMjYA==} engines: {node: '>=20'} typed-array-buffer@1.0.3: resolution: {integrity: sha512-nAYYwfY3qnzX30IkA6AQZjVbtK6duGontcQm1WSG1MD94YLqK0515GNApXkoxKOWMusVssAHWLh9SeaoefYFGw==} engines: {node: '>= 0.4'} typed-array-byte-length@1.0.3: resolution: {integrity: sha512-BaXgOuIxz8n8pIq3e7Atg/7s+DpiYrxn4vdot3w9KbnBhcRQq6o3xemQdIfynqSeXeDrF32x+WvfzmOjPiY9lg==} engines: {node: '>= 0.4'} typed-array-byte-offset@1.0.4: resolution: {integrity: sha512-bTlAFB/FBYMcuX81gbL4OcpH5PmlFHqlCCpAl8AlEzMz5k53oNDvN8p1PNOWLEmI2x4orp3raOFB51tv9X+MFQ==} engines: {node: '>= 0.4'} typed-array-length@1.0.8: resolution: {integrity: sha512-phPGCwqr2+Qo0fwniCE8e4pKnGu/yFb5nD5Y8bf0EEeiI5GklnACYA9GFy/DrAeRrKHXvHn+1SUsOWgJp6RO+g==} engines: {node: '>= 0.4'} typescript-eslint@8.58.2: resolution: {integrity: sha512-V8iSng9mRbdZjl54VJ9NKr6ZB+dW0J3TzRXRGcSbLIej9jV86ZRtlYeTKDR/QLxXykocJ5icNzbsl2+5TzIvcQ==} engines: {node: ^18.18.0 || ^20.9.0 || >=21.1.0} peerDependencies: eslint: ^8.57.0 || ^9.0.0 || ^10.0.0 typescript: '>=4.8.4 <6.1.0' typescript@7.0.2: resolution: {integrity: sha512-8FYau96o3NKOhbjKi/qNvG/W5jhzxkbdm5sj9AbZ/5T5sWqn3hJgLfGx27sRKZWTvyzCP8dLRBTf5tBTSRVUNA==} engines: {node: '>=16.20.0'} hasBin: true uc.micro@2.1.0: resolution: {integrity: sha512-ARDJmphmdvUk6Glw7y9DQ2bFkKBHwQHLi2lsaH6PPmz/Ka9sFOBsBluozhDltWmnv9u/cF6Rt87znRTPV+yp/A==} uglify-js@3.19.3: resolution: {integrity: sha512-v3Xu+yuwBXisp6QYTcH4UbH+xYJXqnq2m/LtQVWKWzYc1iehYnLixoQDN9FH6/j9/oybfd6W9Ghwkl8+UMKTKQ==} engines: {node: '>=0.8.0'} hasBin: true unbox-primitive@1.1.0: resolution: {integrity: sha512-nWJ91DjeOkej/TA8pXQ3myruKpKEYgqvpw9lz4OPHj/NWFNluYrjbz9j01CJ8yKQd2g4jFoOkINCTW2I5LEEyw==} engines: {node: '>= 0.4'} undici-types@8.3.0: resolution: {integrity: sha512-j375ScV60dom+YkPFIfTLcOiPxkN/buHz5GobjLhixFuANaNs3C9l4GmrWqejgXWJ7BbJcFYpTEUkS1Ge8bpZQ==} undici@6.27.0: resolution: {integrity: sha512-YmfV3YnEDzXRC5lZ2jWtWWHKGUm1zIt8AhesR1tens+HTNv+YZlN/dp6G727LOvMJ8xjP9Be7Y2Sdr96LDm+pg==} engines: {node: '>=18.17'} undici@7.28.0: resolution: {integrity: sha512-cRZYrTDwWznlnRiPjggAGxZXanty6M8RV1ff8Wm4LWXBp7/IG8v5DnOm74DtUBp9OONpK75YlPnIjQqX0dBDtA==} engines: {node: '>=20.18.1'} unicode-emoji-modifier-base@1.0.0: resolution: {integrity: sha512-yLSH4py7oFH3oG/9K+XWrz1pSi3dfUrWEnInbxMfArOfc1+33BlGPQtLsOYwvdMy11AwUBetYuaRxSPqgkq+8g==} engines: {node: '>=4'} unicorn-magic@0.1.0: resolution: {integrity: sha512-lRfVq8fE8gz6QMBuDM6a+LO3IAzTi05H6gCVaUpir2E1Rwpo4ZUog45KpNXKC/Mn3Yb9UDuHumeFTo9iV/D9FQ==} engines: {node: '>=18'} unicorn-magic@0.3.0: resolution: {integrity: sha512-+QBBXBCvifc56fsbuxZQ6Sic3wqqc3WWaqxs58gvJrcOuN83HGTCwz3oS5phzU9LthRNE9VrJCFCLUgHeeFnfA==} engines: {node: '>=18'} unicorn-magic@0.4.0: resolution: {integrity: sha512-wH590V9VNgYH9g3lH9wWjTrUoKsjLF6sGLjhR4sH1LWpLmCOH0Zf7PukhDA8BiS7KHe4oPNkcTHqYkj7SOGUOw==} engines: {node: '>=20'} unique-string@3.0.0: resolution: {integrity: sha512-VGXBUVwxKMBUznyffQweQABPRRW1vHZAbadFZud4pLFAqRGvv/96vafgjWFqzourzr8YonlQiPgH0YCJfawoGQ==} engines: {node: '>=12'} universal-user-agent@7.0.3: resolution: {integrity: sha512-TmnEAEAsBJVZM/AADELsK76llnwcf9vMKuPz8JflO1frO8Lchitr0fNaN9d+Ap0BjKtqWqd/J17qeDnXh8CL2A==} universalify@2.0.1: resolution: {integrity: sha512-gptHNQghINnc/vTGIk0SOFGFNXw7JVrlRUtConJRlvaw6DuX0wO5Jeko9sWrMBhh+PsYAZ7oXAiOnf/UKogyiw==} engines: {node: '>= 10.0.0'} unrs-resolver@1.12.2: resolution: {integrity: sha512-dmlRxBJJayXjqTwC+JtF1HhJmgf3ftQ3YejFcZrf4+KKtJv0qDsK1pjqaaVjG7wJ5NJ6UVP1OqRMQ71Z4C3rxQ==} update-browserslist-db@1.2.3: resolution: {integrity: sha512-Js0m9cx+qOgDxo0eMiFGEueWztz+d4+M3rGlmKPT+T4IS/jP4ylw3Nwpu6cpTTP8R1MAC1kF4VbdLt3ARf209w==} hasBin: true peerDependencies: browserslist: '>= 4.21.0' uri-js@4.4.1: resolution: {integrity: sha512-7rKUyy33Q1yc98pQ1DAmLtwX109F7TIfWlW1Ydo8Wl1ii1SeHieeh0HHfPeL2fMXK6z0s8ecKs9frCuLJvndBg==} url-join@5.0.0: resolution: {integrity: sha512-n2huDr9h9yzd6exQVnH/jU5mr+Pfx08LRXXZhkLLetAMESRj+anQsTAh940iMrIetKAmry9coFuZQ2jY8/p3WA==} engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} util-deprecate@1.0.2: resolution: {integrity: sha512-EPD5q1uXyFxJpCrLnCc1nHnq3gOa6DZBocAIiI2TaSCA7VCJ1UJDMagCzIkXNsUYfD1daK//LTEQ8xiIbrHtcw==} validate-npm-package-license@3.0.4: resolution: {integrity: sha512-DpKm2Ui/xN7/HQKCtpZxoRWBhZ9Z0kqtygG8XCgNQ8ZlDnxuQmWhj566j8fN4Cu3/JmbhsDo7fcAJq4s9h27Ew==} vite-plus@0.2.4: resolution: {integrity: sha512-gaBBjOXIq9lLRU44oAYdIr99p+JBLX1kxs+l/6LqGgSXwcVKAdDa1boSrOTELqYCkQQ0fpppXUGWi9o6JDT5zw==} engines: {node: ^20.19.0 || ^22.18.0 || >=24.11.0} hasBin: true peerDependencies: '@vitest/browser-playwright': 4.1.10 '@vitest/browser-webdriverio': 4.1.10 peerDependenciesMeta: '@vitest/browser-playwright': optional: true '@vitest/browser-webdriverio': optional: true vitest@4.1.10: resolution: {integrity: sha512-R9jUTe5S4Qb0HCd4TNqpC7oGcrMssMRGXLW80ubjWsW9VH5GF8y1Y0SFLY9AbqSk6nt0PnOx4H4WNJYZ13GUPw==} engines: {node: ^20.0.0 || ^22.0.0 || >=24.0.0} hasBin: true peerDependencies: '@edge-runtime/vm': '*' '@opentelemetry/api': ^1.9.0 '@types/node': ^20.0.0 || ^22.0.0 || >=24.0.0 '@vitest/browser-playwright': 4.1.10 '@vitest/browser-preview': 4.1.10 '@vitest/browser-webdriverio': 4.1.10 '@vitest/coverage-istanbul': 4.1.10 '@vitest/coverage-v8': 4.1.10 '@vitest/ui': 4.1.10 happy-dom: '*' jsdom: '*' vite: ^6.0.0 || ^7.0.0 || ^8.0.0 peerDependenciesMeta: '@edge-runtime/vm': optional: true '@opentelemetry/api': optional: true '@types/node': optional: true '@vitest/browser-playwright': optional: true '@vitest/browser-preview': optional: true '@vitest/browser-webdriverio': optional: true '@vitest/coverage-istanbul': optional: true '@vitest/coverage-v8': optional: true '@vitest/ui': optional: true happy-dom: optional: true jsdom: optional: true web-worker@1.5.0: resolution: {integrity: sha512-RiMReJrTAiA+mBjGONMnjVDP2u3p9R1vkcGz6gDIrOMT3oGuYwX2WRMYI9ipkphSuE5XKEhydbhNEJh4NY9mlw==} which-boxed-primitive@1.1.1: resolution: {integrity: sha512-TbX3mj8n0odCBFVlY8AxkqcHASw3L60jIuF8jFP78az3C2YhmGvqbHBpAjTRH2/xqYunrJ9g1jSyjCjpoWzIAA==} engines: {node: '>= 0.4'} which-builtin-type@1.2.1: resolution: {integrity: sha512-6iBczoX+kDQ7a3+YJBnh3T+KZRxM/iYNPXicqk66/Qfm1b93iu+yOImkg0zHbj5LNOcNv1TEADiZ0xa34B4q6Q==} engines: {node: '>= 0.4'} which-collection@1.0.2: resolution: {integrity: sha512-K4jVyjnBdgvc86Y6BkaLZEN933SwYOuBFkdmBu9ZfkcAbdVbpITnDmjvZ/aQjRXQrv5EPkTnD1s39GiiqbngCw==} engines: {node: '>= 0.4'} which-typed-array@1.1.22: resolution: {integrity: sha512-fvO4ExWMFsqyhG3AiPAObMuY1lxaqgYcxbc49CNdWDDECOJNgQyvsOWVwbZc+qf3rzRtxojBK+CMEv0Ld5CYpw==} engines: {node: '>= 0.4'} which@2.0.2: resolution: {integrity: sha512-BLI3Tl1TW3Pvl70l3yq3Y64i+awpwXqsGBYWkkqMtnbXgrMD+yj7rhW0kuEDxzJaYXGjEW5ogapKNMEKNMjibA==} engines: {node: '>= 8'} hasBin: true which@7.0.0: resolution: {integrity: sha512-RancgH2dmbLdHl6LRhEqvklWMgl/Hdnun0Y90KhBOLkMefg8Qa7/Zel8Sm+8HEcP6DEjzsWzpkuBQEZok58isA==} engines: {node: ^22.22.2 || ^24.15.0 || >=26.0.0} hasBin: true why-is-node-running@2.3.0: resolution: {integrity: sha512-hUrmaWBdVDcxvYqnyh09zunKzROWjbZTiNy8dBEjkS7ehEDQibXJ7XvlmtbwuTclUiIyN+CyXQD4Vmko8fNm8w==} engines: {node: '>=8'} hasBin: true word-wrap@1.2.5: resolution: {integrity: sha512-BN22B5eaMMI9UMtjrGd5g5eCYPpCPDUy0FJXbYsaT5zYxjFOckS53SQDE3pWkVoWpHXVb3BrYcEN4Twa55B5cA==} engines: {node: '>=0.10.0'} wordwrap@1.0.0: resolution: {integrity: sha512-gvVzJFlPycKc5dZN4yPkP8w7Dc37BtP1yczEneOb4uq34pXZcvrtRTmWV8W+Ume+XCxKgbjM+nevkyFPMybd4Q==} wrap-ansi@10.0.0: resolution: {integrity: sha512-SGcvg80f0wUy2/fXES19feHMz8E0JoXv2uNgHOu4Dgi2OrCy1lqwFYEJz1BLbDI0exjPMe/ZdzZ/YpGECBG/aQ==} engines: {node: '>=20'} wrap-ansi@7.0.0: resolution: {integrity: sha512-YVGIj2kamLSTxw6NsZjoBxfSwsn0ycdesmc4p+Q21c5zPuZ1pl+NfxVdxPtdHvmNVOQ6XSYG4AUtyt/Fi7D16Q==} engines: {node: '>=10'} wrap-ansi@9.0.2: resolution: {integrity: sha512-42AtmgqjV+X1VpdOfyTGOYRi0/zsoLqtXQckTmqTeybT+BDIbM/Guxo7x3pE2vtpr1ok6xRqM9OpBe+Jyoqyww==} engines: {node: '>=18'} ws@8.21.0: resolution: {integrity: sha512-Vsp28b7DRcimFQvrqu2Wek3z1iYxDCWqHYB8Qsnk/S4RfaCQzPGPyBNuVjJV3cd6UiKtUtp6sNM77gWvzcCH+g==} engines: {node: '>=10.0.0'} peerDependencies: bufferutil: ^4.0.1 utf-8-validate: '>=5.0.2' peerDependenciesMeta: bufferutil: optional: true utf-8-validate: optional: true xtend@4.0.2: resolution: {integrity: sha512-LKYU1iAXJXUgAXn9URjiu+MWhyUXHsvfp7mcuYm9dSUKK0/CjtrUwFAxD82/mCWbtLsGjFIad0wIsod4zrTAEQ==} engines: {node: '>=0.4'} y18n@5.0.8: resolution: {integrity: sha512-0pfFzegeDWJHJIAmTLRP2DwHjdF5s7jo9tuztdQxAhINCdvS+3nGINqPd00AphqJR/0LhANUS6/+7SCb98YOfA==} engines: {node: '>=10'} yallist@3.1.1: resolution: {integrity: sha512-a4UGQaWPH59mOXUYnAG2ewncQS4i4F43Tv3JoAM+s2VDAmS9NsK8GpDMLrCHPksFT7h3K6TOoUNn2pb7RoXx4g==} yaml@2.9.0: resolution: {integrity: sha512-2AvhNX3mb8zd6Zy7INTtSpl1F15HW6Wnqj0srWlkKLcpYl/gMIMJiyuGq2KeI2YFxUPjdlB+3Lc10seMLtL4cA==} engines: {node: '>= 14.6'} hasBin: true yargs-parser@20.2.9: resolution: {integrity: sha512-y11nGElTIV+CT3Zv9t7VKl+Q3hTQoT9a1Qzezhhl6Rp21gJ/IVTW7Z3y9EWXhuUBC2Shnf+DX0antecpAwSP8w==} engines: {node: '>=10'} yargs-parser@22.0.0: resolution: {integrity: sha512-rwu/ClNdSMpkSrUb+d6BRsSkLUq1fmfsY6TOpYzTwvwkg1/NRG85KBy3kq++A8LKQwX6lsu+aWad+2khvuXrqw==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yargs@16.2.2: resolution: {integrity: sha512-Nt9ZJjXTv5R8MHbqby/wXQ6Gi0Bb3TcYZkR1bzuL4yB2OxWPkXknz513gEF0GoA6tn00UpbPvERW8rzCuWCA6w==} engines: {node: '>=10'} yargs@18.0.0: resolution: {integrity: sha512-4UEqdc2RYGHZc7Doyqkrqiln3p9X2DZVxaGbwhn2pi7MrRagKaOcIKe8L3OxYcbhXLgLFUS3zAYuQjKBQgmuNg==} engines: {node: ^20.19.0 || ^22.12.0 || >=23} yocto-queue@0.1.0: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} yoctocolors@2.1.2: resolution: {integrity: sha512-CzhO+pFNo8ajLM2d2IW/R93ipy99LWjtwblvC1RsoSUMZgyLbYFr221TnSNT7GjGdYui6P459mw9JH/g/zW2ug==} engines: {node: '>=18'} zod-validation-error@4.0.2: resolution: {integrity: sha512-Q6/nZLe6jxuU80qb/4uJ4t5v2VEZ44lzQjPDhYJNztRQ4wyWc6VF3D3Kb/fAuPetZQnhS3hnajCf9CsWesghLQ==} engines: {node: '>=18.0.0'} peerDependencies: zod: ^3.25.0 || ^4.0.0 zod@4.4.3: resolution: {integrity: sha512-ytENFjIJFl2UwYglde2jchW2Hwm4GJFLDiSXWdTrJQBIN9Fcyp7n4DhxJEiWNAJMV1/BqWfW/kkg71UDcHJyTQ==} snapshots: '@actions/core@3.0.1': dependencies: '@actions/exec': 3.0.0 '@actions/http-client': 4.0.1 '@actions/exec@3.0.0': dependencies: '@actions/io': 3.0.2 '@actions/http-client@4.0.1': dependencies: tunnel: 0.0.6 undici: 6.27.0 '@actions/io@3.0.2': {} '@babel/code-frame@7.29.7': dependencies: '@babel/helper-validator-identifier': 7.29.7 js-tokens: 4.0.0 picocolors: 1.1.1 '@babel/compat-data@7.29.7': {} '@babel/core@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-compilation-targets': 7.29.7 '@babel/helper-module-transforms': 7.29.7(@babel/core@7.29.7) '@babel/helpers': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 '@jridgewell/remapping': 2.3.5 convert-source-map: 2.0.0 debug: 4.4.3 gensync: 1.0.0-beta.2 json5: 2.2.3 semver: 6.3.1 transitivePeerDependencies: - supports-color '@babel/generator@7.29.7': dependencies: '@babel/parser': 7.29.7 '@babel/types': 7.29.7 '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 jsesc: 3.1.0 '@babel/helper-compilation-targets@7.29.7': dependencies: '@babel/compat-data': 7.29.7 '@babel/helper-validator-option': 7.29.7 browserslist: 4.28.5 lru-cache: 5.1.1 semver: 6.3.1 '@babel/helper-globals@7.29.7': {} '@babel/helper-module-imports@7.29.7': dependencies: '@babel/traverse': 7.29.7 '@babel/types': 7.29.7 transitivePeerDependencies: - supports-color '@babel/helper-module-transforms@7.29.7(@babel/core@7.29.7)': dependencies: '@babel/core': 7.29.7 '@babel/helper-module-imports': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@babel/traverse': 7.29.7 transitivePeerDependencies: - supports-color '@babel/helper-string-parser@7.29.7': {} '@babel/helper-validator-identifier@7.29.7': {} '@babel/helper-validator-option@7.29.7': {} '@babel/helpers@7.29.7': dependencies: '@babel/template': 7.29.7 '@babel/types': 7.29.7 '@babel/parser@7.29.7': dependencies: '@babel/types': 7.29.7 '@babel/runtime@7.29.7': {} '@babel/template@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/parser': 7.29.7 '@babel/types': 7.29.7 '@babel/traverse@7.29.7': dependencies: '@babel/code-frame': 7.29.7 '@babel/generator': 7.29.7 '@babel/helper-globals': 7.29.7 '@babel/parser': 7.29.7 '@babel/template': 7.29.7 '@babel/types': 7.29.7 debug: 4.4.3 transitivePeerDependencies: - supports-color '@babel/types@7.29.7': dependencies: '@babel/helper-string-parser': 7.29.7 '@babel/helper-validator-identifier': 7.29.7 '@blazediff/core@1.9.1': {} '@colors/colors@1.5.0': optional: true '@emnapi/core@1.10.0': dependencies: '@emnapi/wasi-threads': 1.2.1 tslib: 2.8.1 optional: true '@emnapi/runtime@1.10.0': dependencies: tslib: 2.8.1 optional: true '@emnapi/wasi-threads@1.2.1': dependencies: tslib: 2.8.1 optional: true '@es-joy/jsdoccomment@0.86.0': dependencies: '@types/estree': 1.0.9 '@typescript-eslint/types': 8.63.0 comment-parser: 1.4.6 esquery: 1.7.0 jsdoc-type-pratt-parser: 7.2.0 '@es-joy/resolve.exports@1.2.0': {} '@eslint-community/eslint-utils@4.9.1(eslint@10.6.0)': dependencies: eslint: 10.6.0 eslint-visitor-keys: 3.4.3 '@eslint-community/regexpp@4.12.2': {} '@eslint/compat@2.0.5(eslint@10.6.0)': dependencies: '@eslint/core': 1.2.1 optionalDependencies: eslint: 10.6.0 '@eslint/config-array@0.23.5': dependencies: '@eslint/object-schema': 3.0.5 debug: 4.4.3 minimatch: 10.2.5 transitivePeerDependencies: - supports-color '@eslint/config-helpers@0.6.0': dependencies: '@eslint/core': 1.2.1 '@eslint/core@1.2.1': dependencies: '@types/json-schema': 7.0.15 '@eslint/js@10.0.1(eslint@10.6.0)': optionalDependencies: eslint: 10.6.0 '@eslint/object-schema@3.0.5': {} '@eslint/plugin-kit@0.7.2': dependencies: '@eslint/core': 1.2.1 levn: 0.4.1 '@humanfs/core@0.19.2': dependencies: '@humanfs/types': 0.15.0 '@humanfs/node@0.16.8': dependencies: '@humanfs/core': 0.19.2 '@humanfs/types': 0.15.0 '@humanwhocodes/retry': 0.4.3 '@humanfs/types@0.15.0': {} '@humanwhocodes/module-importer@1.0.1': {} '@humanwhocodes/retry@0.4.3': {} '@jridgewell/gen-mapping@0.3.13': dependencies: '@jridgewell/sourcemap-codec': 1.5.5 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/remapping@2.3.5': dependencies: '@jridgewell/gen-mapping': 0.3.13 '@jridgewell/trace-mapping': 0.3.31 '@jridgewell/resolve-uri@3.1.2': {} '@jridgewell/sourcemap-codec@1.5.5': {} '@jridgewell/trace-mapping@0.3.31': dependencies: '@jridgewell/resolve-uri': 3.1.2 '@jridgewell/sourcemap-codec': 1.5.5 '@napi-rs/wasm-runtime@1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0)': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@tybys/wasm-util': 0.10.3 optional: true '@next/eslint-plugin-next@16.2.4': dependencies: fast-glob: 3.3.1 '@nodelib/fs.scandir@2.1.5': dependencies: '@nodelib/fs.stat': 2.0.5 run-parallel: 1.2.0 '@nodelib/fs.stat@2.0.5': {} '@nodelib/fs.walk@1.2.8': dependencies: '@nodelib/fs.scandir': 2.1.5 fastq: 1.20.1 '@octokit/auth-token@6.0.0': {} '@octokit/core@7.0.6': dependencies: '@octokit/auth-token': 6.0.0 '@octokit/graphql': 9.0.3 '@octokit/request': 10.0.11 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 before-after-hook: 4.0.0 universal-user-agent: 7.0.3 '@octokit/endpoint@11.0.3': dependencies: '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 '@octokit/graphql@9.0.3': dependencies: '@octokit/request': 10.0.11 '@octokit/types': 16.0.0 universal-user-agent: 7.0.3 '@octokit/openapi-types@27.0.0': {} '@octokit/plugin-paginate-rest@14.0.0(@octokit/core@7.0.6)': dependencies: '@octokit/core': 7.0.6 '@octokit/types': 16.0.0 '@octokit/plugin-retry@8.1.0(@octokit/core@7.0.6)': dependencies: '@octokit/core': 7.0.6 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 bottleneck: 2.19.5 '@octokit/plugin-throttling@11.0.3(@octokit/core@7.0.6)': dependencies: '@octokit/core': 7.0.6 '@octokit/types': 16.0.0 bottleneck: 2.19.5 '@octokit/request-error@7.1.0': dependencies: '@octokit/types': 16.0.0 '@octokit/request@10.0.11': dependencies: '@octokit/endpoint': 11.0.3 '@octokit/request-error': 7.1.0 '@octokit/types': 16.0.0 content-type: 2.0.0 json-with-bigint: 3.5.8 universal-user-agent: 7.0.3 '@octokit/types@16.0.0': dependencies: '@octokit/openapi-types': 27.0.0 '@oxc-project/runtime@0.138.0': {} '@oxc-project/types@0.138.0': {} '@oxfmt/binding-android-arm-eabi@0.57.0': optional: true '@oxfmt/binding-android-arm64@0.57.0': optional: true '@oxfmt/binding-darwin-arm64@0.57.0': optional: true '@oxfmt/binding-darwin-x64@0.57.0': optional: true '@oxfmt/binding-freebsd-x64@0.57.0': optional: true '@oxfmt/binding-linux-arm-gnueabihf@0.57.0': optional: true '@oxfmt/binding-linux-arm-musleabihf@0.57.0': optional: true '@oxfmt/binding-linux-arm64-gnu@0.57.0': optional: true '@oxfmt/binding-linux-arm64-musl@0.57.0': optional: true '@oxfmt/binding-linux-ppc64-gnu@0.57.0': optional: true '@oxfmt/binding-linux-riscv64-gnu@0.57.0': optional: true '@oxfmt/binding-linux-riscv64-musl@0.57.0': optional: true '@oxfmt/binding-linux-s390x-gnu@0.57.0': optional: true '@oxfmt/binding-linux-x64-gnu@0.57.0': optional: true '@oxfmt/binding-linux-x64-musl@0.57.0': optional: true '@oxfmt/binding-openharmony-arm64@0.57.0': optional: true '@oxfmt/binding-win32-arm64-msvc@0.57.0': optional: true '@oxfmt/binding-win32-ia32-msvc@0.57.0': optional: true '@oxfmt/binding-win32-x64-msvc@0.57.0': optional: true '@oxlint-tsgolint/darwin-arm64@0.24.0': optional: true '@oxlint-tsgolint/darwin-x64@0.24.0': optional: true '@oxlint-tsgolint/linux-arm64@0.24.0': optional: true '@oxlint-tsgolint/linux-x64@0.24.0': optional: true '@oxlint-tsgolint/win32-arm64@0.24.0': optional: true '@oxlint-tsgolint/win32-x64@0.24.0': optional: true '@oxlint/binding-android-arm-eabi@1.72.0': optional: true '@oxlint/binding-android-arm64@1.72.0': optional: true '@oxlint/binding-darwin-arm64@1.72.0': optional: true '@oxlint/binding-darwin-x64@1.72.0': optional: true '@oxlint/binding-freebsd-x64@1.72.0': optional: true '@oxlint/binding-linux-arm-gnueabihf@1.72.0': optional: true '@oxlint/binding-linux-arm-musleabihf@1.72.0': optional: true '@oxlint/binding-linux-arm64-gnu@1.72.0': optional: true '@oxlint/binding-linux-arm64-musl@1.72.0': optional: true '@oxlint/binding-linux-ppc64-gnu@1.72.0': optional: true '@oxlint/binding-linux-riscv64-gnu@1.72.0': optional: true '@oxlint/binding-linux-riscv64-musl@1.72.0': optional: true '@oxlint/binding-linux-s390x-gnu@1.72.0': optional: true '@oxlint/binding-linux-x64-gnu@1.72.0': optional: true '@oxlint/binding-linux-x64-musl@1.72.0': optional: true '@oxlint/binding-openharmony-arm64@1.72.0': optional: true '@oxlint/binding-win32-arm64-msvc@1.72.0': optional: true '@oxlint/binding-win32-ia32-msvc@1.72.0': optional: true '@oxlint/binding-win32-x64-msvc@1.72.0': optional: true '@oxlint/plugins@1.68.0': {} '@package-json/types@0.0.12': {} '@pkgr/core@0.3.6': {} '@pnpm/config.env-replace@1.1.0': {} '@pnpm/network.ca-file@1.0.2': dependencies: graceful-fs: 4.2.10 '@pnpm/npm-conf@3.0.3': dependencies: '@pnpm/config.env-replace': 1.1.0 '@pnpm/network.ca-file': 1.0.2 config-chain: 1.1.13 '@polka/url@1.0.0-next.29': {} '@sec-ant/readable-stream@0.4.1': {} '@semantic-release/changelog@6.0.3(semantic-release@25.0.5(typescript@7.0.2))': dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 fs-extra: 11.3.6 lodash: 4.18.1 semantic-release: 25.0.5(typescript@7.0.2) '@semantic-release/commit-analyzer@13.0.1(semantic-release@25.0.5(typescript@7.0.2))': dependencies: conventional-changelog-angular: 8.3.1 conventional-changelog-writer: 8.4.0 conventional-commits-filter: 5.0.0 conventional-commits-parser: 6.4.0 debug: 4.4.3 import-from-esm: 2.0.0 lodash-es: 4.18.1 micromatch: 4.0.8 semantic-release: 25.0.5(typescript@7.0.2) transitivePeerDependencies: - supports-color '@semantic-release/error@3.0.0': {} '@semantic-release/error@4.0.0': {} '@semantic-release/git@10.0.1(semantic-release@25.0.5(typescript@7.0.2))': dependencies: '@semantic-release/error': 3.0.0 aggregate-error: 3.1.0 debug: 4.4.3 dir-glob: 3.0.1 execa: 5.1.1 lodash: 4.18.1 micromatch: 4.0.8 p-reduce: 2.1.0 semantic-release: 25.0.5(typescript@7.0.2) transitivePeerDependencies: - supports-color '@semantic-release/github@12.0.9(semantic-release@25.0.5(typescript@7.0.2))': dependencies: '@octokit/core': 7.0.6 '@octokit/plugin-paginate-rest': 14.0.0(@octokit/core@7.0.6) '@octokit/plugin-retry': 8.1.0(@octokit/core@7.0.6) '@octokit/plugin-throttling': 11.0.3(@octokit/core@7.0.6) '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 debug: 4.4.3 dir-glob: 3.0.1 http-proxy-agent: 9.1.0 https-proxy-agent: 9.1.0 issue-parser: 7.0.2 lodash-es: 4.18.1 mime: 4.1.0 p-filter: 4.1.0 semantic-release: 25.0.5(typescript@7.0.2) tinyglobby: 0.2.17 undici: 7.28.0 url-join: 5.0.0 transitivePeerDependencies: - kerberos - supports-color '@semantic-release/npm@13.1.5(semantic-release@25.0.5(typescript@7.0.2))': dependencies: '@actions/core': 3.0.1 '@semantic-release/error': 4.0.0 aggregate-error: 5.0.0 env-ci: 11.2.0 execa: 9.6.1 fs-extra: 11.3.6 lodash-es: 4.18.1 nerf-dart: 1.0.0 normalize-url: 9.0.1 npm: 11.18.0 rc: 1.2.8 read-pkg: 10.1.0 registry-auth-token: 5.1.1 semantic-release: 25.0.5(typescript@7.0.2) semver: 7.8.5 tempy: 3.2.0 '@semantic-release/release-notes-generator@14.1.1(semantic-release@25.0.5(typescript@7.0.2))': dependencies: conventional-changelog-angular: 8.3.1 conventional-changelog-writer: 8.4.0 conventional-commits-filter: 5.0.0 conventional-commits-parser: 6.4.0 debug: 4.4.3 import-from-esm: 2.0.0 lodash-es: 4.18.1 read-package-up: 11.0.0 semantic-release: 25.0.5(typescript@7.0.2) transitivePeerDependencies: - supports-color '@simple-libs/stream-utils@1.2.0': {} '@sindresorhus/base62@1.0.0': {} '@sindresorhus/is@4.6.0': {} '@sindresorhus/merge-streams@4.0.0': {} '@standard-schema/spec@1.1.0': {} '@stylistic/eslint-plugin@5.10.0(eslint@10.6.0)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@typescript-eslint/types': 8.63.0 eslint: 10.6.0 eslint-visitor-keys: 4.2.1 espree: 10.4.0 estraverse: 5.3.0 picomatch: 4.0.5 '@testing-library/dom@10.4.1': dependencies: '@babel/code-frame': 7.29.7 '@babel/runtime': 7.29.7 '@types/aria-query': 5.0.4 aria-query: 5.3.0 dom-accessibility-api: 0.5.16 lz-string: 1.5.0 picocolors: 1.1.1 pretty-format: 27.5.1 '@testing-library/user-event@14.6.1(@testing-library/dom@10.4.1)': dependencies: '@testing-library/dom': 10.4.1 '@tybys/wasm-util@0.10.3': dependencies: tslib: 2.8.1 optional: true '@types/aria-query@5.0.4': {} '@types/chai@5.2.3': dependencies: '@types/deep-eql': 4.0.2 assertion-error: 2.0.1 '@types/debug@4.1.13': dependencies: '@types/ms': 2.1.0 '@types/deep-eql@4.0.2': {} '@types/esrecurse@4.3.1': {} '@types/estree@1.0.9': {} '@types/json-schema@7.0.15': {} '@types/katex@0.16.8': {} '@types/ms@2.1.0': {} '@types/node@26.1.1': dependencies: undici-types: 8.3.0 '@types/normalize-package-data@2.4.4': {} '@types/unist@2.0.11': {} '@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0)(typescript@7.0.2)': dependencies: '@eslint-community/regexpp': 4.12.2 '@typescript-eslint/parser': 8.58.2(eslint@10.6.0)(typescript@7.0.2) '@typescript-eslint/scope-manager': 8.58.2 '@typescript-eslint/type-utils': 8.58.2(eslint@10.6.0)(typescript@7.0.2) '@typescript-eslint/utils': 8.58.2(eslint@10.6.0)(typescript@7.0.2) '@typescript-eslint/visitor-keys': 8.58.2 eslint: 10.6.0 ignore: 7.0.6 natural-compare: 1.4.0 ts-api-utils: 2.5.0(typescript@7.0.2) typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2)': dependencies: '@typescript-eslint/scope-manager': 8.58.2 '@typescript-eslint/types': 8.58.2 '@typescript-eslint/typescript-estree': 8.58.2(typescript@7.0.2) '@typescript-eslint/visitor-keys': 8.58.2 debug: 4.4.3 eslint: 10.6.0 typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/project-service@8.58.2(typescript@7.0.2)': dependencies: '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@7.0.2) '@typescript-eslint/types': 8.63.0 debug: 4.4.3 typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/scope-manager@8.58.2': dependencies: '@typescript-eslint/types': 8.58.2 '@typescript-eslint/visitor-keys': 8.58.2 '@typescript-eslint/tsconfig-utils@8.58.2(typescript@7.0.2)': dependencies: typescript: 7.0.2 '@typescript-eslint/type-utils@8.58.2(eslint@10.6.0)(typescript@7.0.2)': dependencies: '@typescript-eslint/types': 8.58.2 '@typescript-eslint/typescript-estree': 8.58.2(typescript@7.0.2) '@typescript-eslint/utils': 8.58.2(eslint@10.6.0)(typescript@7.0.2) debug: 4.4.3 eslint: 10.6.0 ts-api-utils: 2.5.0(typescript@7.0.2) typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/types@8.58.2': {} '@typescript-eslint/types@8.63.0': {} '@typescript-eslint/typescript-estree@8.58.2(typescript@7.0.2)': dependencies: '@typescript-eslint/project-service': 8.58.2(typescript@7.0.2) '@typescript-eslint/tsconfig-utils': 8.58.2(typescript@7.0.2) '@typescript-eslint/types': 8.58.2 '@typescript-eslint/visitor-keys': 8.58.2 debug: 4.4.3 minimatch: 10.2.5 semver: 7.8.5 tinyglobby: 0.2.17 ts-api-utils: 2.5.0(typescript@7.0.2) typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/utils@8.58.2(eslint@10.6.0)(typescript@7.0.2)': dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@typescript-eslint/scope-manager': 8.58.2 '@typescript-eslint/types': 8.58.2 '@typescript-eslint/typescript-estree': 8.58.2(typescript@7.0.2) eslint: 10.6.0 typescript: 7.0.2 transitivePeerDependencies: - supports-color '@typescript-eslint/visitor-keys@8.58.2': dependencies: '@typescript-eslint/types': 8.58.2 eslint-visitor-keys: 5.0.1 '@typescript/typescript-aix-ppc64@7.0.2': optional: true '@typescript/typescript-darwin-arm64@7.0.2': optional: true '@typescript/typescript-darwin-x64@7.0.2': optional: true '@typescript/typescript-freebsd-arm64@7.0.2': optional: true '@typescript/typescript-freebsd-x64@7.0.2': optional: true '@typescript/typescript-linux-arm64@7.0.2': optional: true '@typescript/typescript-linux-arm@7.0.2': optional: true '@typescript/typescript-linux-loong64@7.0.2': optional: true '@typescript/typescript-linux-mips64el@7.0.2': optional: true '@typescript/typescript-linux-ppc64@7.0.2': optional: true '@typescript/typescript-linux-riscv64@7.0.2': optional: true '@typescript/typescript-linux-s390x@7.0.2': optional: true '@typescript/typescript-linux-x64@7.0.2': optional: true '@typescript/typescript-netbsd-arm64@7.0.2': optional: true '@typescript/typescript-netbsd-x64@7.0.2': optional: true '@typescript/typescript-openbsd-arm64@7.0.2': optional: true '@typescript/typescript-openbsd-x64@7.0.2': optional: true '@typescript/typescript-sunos-x64@7.0.2': optional: true '@typescript/typescript-win32-arm64@7.0.2': optional: true '@typescript/typescript-win32-x64@7.0.2': optional: true '@unrs/resolver-binding-android-arm-eabi@1.12.2': optional: true '@unrs/resolver-binding-android-arm64@1.12.2': optional: true '@unrs/resolver-binding-darwin-arm64@1.12.2': optional: true '@unrs/resolver-binding-darwin-x64@1.12.2': optional: true '@unrs/resolver-binding-freebsd-x64@1.12.2': optional: true '@unrs/resolver-binding-linux-arm-gnueabihf@1.12.2': optional: true '@unrs/resolver-binding-linux-arm-musleabihf@1.12.2': optional: true '@unrs/resolver-binding-linux-arm64-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-arm64-musl@1.12.2': optional: true '@unrs/resolver-binding-linux-loong64-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-loong64-musl@1.12.2': optional: true '@unrs/resolver-binding-linux-ppc64-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-riscv64-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-riscv64-musl@1.12.2': optional: true '@unrs/resolver-binding-linux-s390x-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-x64-gnu@1.12.2': optional: true '@unrs/resolver-binding-linux-x64-musl@1.12.2': optional: true '@unrs/resolver-binding-openharmony-arm64@1.12.2': optional: true '@unrs/resolver-binding-wasm32-wasi@1.12.2': dependencies: '@emnapi/core': 1.10.0 '@emnapi/runtime': 1.10.0 '@napi-rs/wasm-runtime': 1.1.6(@emnapi/core@1.10.0)(@emnapi/runtime@1.10.0) optional: true '@unrs/resolver-binding-win32-arm64-msvc@1.12.2': optional: true '@unrs/resolver-binding-win32-ia32-msvc@1.12.2': optional: true '@unrs/resolver-binding-win32-x64-msvc@1.12.2': optional: true '@vitest/browser-preview@4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10)': dependencies: '@testing-library/dom': 10.4.1 '@testing-library/user-event': 14.6.1(@testing-library/dom@10.4.1) '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10) vitest: 4.1.10(@types/node@26.1.1)(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite '@vitest/browser@4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10)': dependencies: '@blazediff/core': 1.9.1 '@vitest/mocker': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/utils': 4.1.10 magic-string: 0.30.21 pngjs: 7.0.0 sirv: 3.0.2 tinyrainbow: 3.1.0 vitest: 4.1.10(@types/node@26.1.1)(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) ws: 8.21.0 transitivePeerDependencies: - bufferutil - msw - utf-8-validate - vite '@vitest/expect@4.1.10': dependencies: '@standard-schema/spec': 1.1.0 '@types/chai': 5.2.3 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 chai: 6.2.2 tinyrainbow: 3.1.0 '@vitest/mocker@4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))': dependencies: '@vitest/spy': 4.1.10 estree-walker: 3.0.3 magic-string: 0.30.21 optionalDependencies: vite: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)' '@vitest/pretty-format@4.1.10': dependencies: tinyrainbow: 3.1.0 '@vitest/runner@4.1.10': dependencies: '@vitest/utils': 4.1.10 pathe: 2.0.3 '@vitest/snapshot@4.1.10': dependencies: '@vitest/pretty-format': 4.1.10 '@vitest/utils': 4.1.10 magic-string: 0.30.21 pathe: 2.0.3 '@vitest/spy@4.1.10': {} '@vitest/utils@4.1.10': dependencies: '@vitest/pretty-format': 4.1.10 convert-source-map: 2.0.0 tinyrainbow: 3.1.0 '@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)': dependencies: '@oxc-project/runtime': 0.138.0 '@oxc-project/types': 0.138.0 lightningcss: 1.32.0 postcss: 8.5.16 optionalDependencies: '@types/node': 26.1.1 fsevents: 2.3.3 typescript: 7.0.2 yaml: 2.9.0 '@voidzero-dev/vite-plus-darwin-arm64@0.2.4': optional: true '@voidzero-dev/vite-plus-darwin-x64@0.2.4': optional: true '@voidzero-dev/vite-plus-linux-arm64-gnu@0.2.4': optional: true '@voidzero-dev/vite-plus-linux-arm64-musl@0.2.4': optional: true '@voidzero-dev/vite-plus-linux-x64-gnu@0.2.4': optional: true '@voidzero-dev/vite-plus-linux-x64-musl@0.2.4': optional: true '@voidzero-dev/vite-plus-win32-arm64-msvc@0.2.4': optional: true '@voidzero-dev/vite-plus-win32-x64-msvc@0.2.4': optional: true acorn-jsx@5.3.2(acorn@8.17.0): dependencies: acorn: 8.17.0 acorn@8.17.0: {} agent-base@9.0.0: {} aggregate-error@3.1.0: dependencies: clean-stack: 2.2.0 indent-string: 4.0.0 aggregate-error@5.0.0: dependencies: clean-stack: 5.3.0 indent-string: 5.0.0 ajv@6.15.0: dependencies: fast-deep-equal: 3.1.3 fast-json-stable-stringify: 2.1.0 json-schema-traverse: 0.4.1 uri-js: 4.4.1 ansi-escapes@7.3.0: dependencies: environment: 1.1.0 ansi-regex@5.0.1: {} ansi-regex@6.2.2: {} ansi-styles@3.2.1: dependencies: color-convert: 1.9.3 ansi-styles@4.3.0: dependencies: color-convert: 2.0.1 ansi-styles@5.2.0: {} ansi-styles@6.2.3: {} any-promise@1.3.0: {} are-docs-informative@0.0.2: {} argparse@2.0.1: {} argv-formatter@1.0.0: {} aria-query@5.3.0: dependencies: dequal: 2.0.3 aria-query@5.3.2: {} array-buffer-byte-length@1.0.2: dependencies: call-bound: 1.0.4 is-array-buffer: 3.0.5 array-ify@1.0.0: {} array-includes@3.1.9: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-abstract: 1.24.2 es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 is-string: 1.1.1 math-intrinsics: 1.1.0 array.prototype.flat@1.3.3: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 array.prototype.flatmap@1.3.3: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-shim-unscopables: 1.1.0 arraybuffer.prototype.slice@1.0.4: dependencies: array-buffer-byte-length: 1.0.2 call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 get-intrinsic: 1.3.0 is-array-buffer: 3.0.5 assertion-error@2.0.1: {} ast-types-flow@0.0.8: {} async-function@1.0.0: {} available-typed-arrays@1.0.7: dependencies: possible-typed-array-names: 1.1.0 axe-core@4.12.1: {} axobject-query@4.1.0: {} balanced-match@1.0.2: {} balanced-match@4.0.4: {} baseline-browser-mapping@2.10.42: {} before-after-hook@4.0.0: {} bottleneck@2.19.5: {} brace-expansion@1.1.16: dependencies: balanced-match: 1.0.2 concat-map: 0.0.1 brace-expansion@5.0.7: dependencies: balanced-match: 4.0.4 braces@3.0.3: dependencies: fill-range: 7.1.1 browserslist@4.28.5: dependencies: baseline-browser-mapping: 2.10.42 caniuse-lite: 1.0.30001803 electron-to-chromium: 1.5.389 node-releases: 2.0.51 update-browserslist-db: 1.2.3(browserslist@4.28.5) builtin-modules@5.3.0: {} call-bind-apply-helpers@1.0.2: dependencies: es-errors: 1.3.0 function-bind: 1.1.2 call-bind@1.0.9: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 get-intrinsic: 1.3.0 set-function-length: 1.2.2 call-bound@1.0.4: dependencies: call-bind-apply-helpers: 1.0.2 get-intrinsic: 1.3.0 callsites@3.1.0: {} caniuse-lite@1.0.30001803: {} chai@6.2.2: {} chalk@2.4.2: dependencies: ansi-styles: 3.2.1 escape-string-regexp: 1.0.5 supports-color: 5.5.0 chalk@4.1.2: dependencies: ansi-styles: 4.3.0 supports-color: 7.2.0 chalk@5.6.2: {} change-case@5.4.4: {} char-regex@1.0.2: {} character-entities-legacy@3.0.0: {} character-entities@2.0.2: {} character-reference-invalid@2.0.1: {} ci-info@4.4.0: {} clean-regexp@1.0.0: dependencies: escape-string-regexp: 1.0.5 clean-stack@2.2.0: {} clean-stack@5.3.0: dependencies: escape-string-regexp: 5.0.0 cli-cursor@5.0.0: dependencies: restore-cursor: 5.1.0 cli-highlight@2.1.11: dependencies: chalk: 4.1.2 highlight.js: 10.7.3 mz: 2.7.0 parse5: 5.1.1 parse5-htmlparser2-tree-adapter: 6.0.1 yargs: 16.2.2 cli-table3@0.6.5: dependencies: string-width: 4.2.3 optionalDependencies: '@colors/colors': 1.5.0 cli-truncate@5.2.0: dependencies: slice-ansi: 8.0.0 string-width: 8.2.2 cliui@7.0.4: dependencies: string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi: 7.0.0 cliui@9.0.1: dependencies: string-width: 7.2.0 strip-ansi: 7.2.0 wrap-ansi: 9.0.2 color-convert@1.9.3: dependencies: color-name: 1.1.3 color-convert@2.0.1: dependencies: color-name: 1.1.4 color-name@1.1.3: {} color-name@1.1.4: {} commander@15.0.0: {} commander@8.3.0: {} comment-parser@1.4.6: {} comment-parser@1.4.7: {} compare-func@2.0.0: dependencies: array-ify: 1.0.0 dot-prop: 5.3.0 concat-map@0.0.1: {} config-chain@1.1.13: dependencies: ini: 1.3.8 proto-list: 1.2.4 content-type@2.0.0: {} conventional-changelog-angular@8.3.1: dependencies: compare-func: 2.0.0 conventional-changelog-writer@8.4.0: dependencies: '@simple-libs/stream-utils': 1.2.0 conventional-commits-filter: 5.0.0 handlebars: 4.7.9 meow: 13.2.0 semver: 7.8.5 conventional-commits-filter@5.0.0: {} conventional-commits-parser@6.4.0: dependencies: '@simple-libs/stream-utils': 1.2.0 meow: 13.2.0 convert-hrtime@5.0.0: {} convert-source-map@2.0.0: {} core-js-compat@3.49.0: dependencies: browserslist: 4.28.5 core-util-is@1.0.3: {} cosmiconfig@9.0.2(typescript@7.0.2): dependencies: env-paths: 2.2.1 import-fresh: 3.3.1 js-yaml: 4.3.0 parse-json: 5.2.0 optionalDependencies: typescript: 7.0.2 cross-spawn@7.0.6: dependencies: path-key: 3.1.1 shebang-command: 2.0.0 which: 2.0.2 crypto-random-string@4.0.0: dependencies: type-fest: 1.4.0 damerau-levenshtein@1.0.8: {} data-view-buffer@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-length@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 data-view-byte-offset@1.0.1: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-data-view: 1.0.2 debug@4.4.3: dependencies: ms: 2.1.3 decode-named-character-reference@1.3.0: dependencies: character-entities: 2.0.2 deep-extend@0.6.0: {} deep-is@0.1.4: {} define-data-property@1.1.4: dependencies: es-define-property: 1.0.1 es-errors: 1.3.0 gopd: 1.2.0 define-properties@1.2.1: dependencies: define-data-property: 1.1.4 has-property-descriptors: 1.0.2 object-keys: 1.1.1 dequal@2.0.3: {} detect-libc@2.1.2: {} devlop@1.1.0: dependencies: dequal: 2.0.3 dir-glob@3.0.1: dependencies: path-type: 4.0.0 dom-accessibility-api@0.5.16: {} dot-prop@5.3.0: dependencies: is-obj: 2.0.0 dunder-proto@1.0.1: dependencies: call-bind-apply-helpers: 1.0.2 es-errors: 1.3.0 gopd: 1.2.0 duplexer2@0.1.4: dependencies: readable-stream: 2.3.8 electron-to-chromium@1.5.389: {} emoji-regex@10.6.0: {} emoji-regex@8.0.0: {} emoji-regex@9.2.2: {} emojilib@2.4.0: {} entities@4.5.0: {} env-ci@11.2.0: dependencies: execa: 8.0.1 java-properties: 1.0.2 env-paths@2.2.1: {} environment@1.1.0: {} error-ex@1.3.4: dependencies: is-arrayish: 0.2.1 es-abstract-get@1.0.0: dependencies: es-errors: 1.3.0 es-object-atoms: 1.1.2 is-callable: 1.2.7 object-inspect: 1.13.4 es-abstract@1.24.2: dependencies: array-buffer-byte-length: 1.0.2 arraybuffer.prototype.slice: 1.0.4 available-typed-arrays: 1.0.7 call-bind: 1.0.9 call-bound: 1.0.4 data-view-buffer: 1.0.2 data-view-byte-length: 1.0.2 data-view-byte-offset: 1.0.1 es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.2 es-set-tostringtag: 2.1.0 es-to-primitive: 1.3.4 function.prototype.name: 1.2.0 get-intrinsic: 1.3.0 get-proto: 1.0.1 get-symbol-description: 1.1.0 globalthis: 1.0.4 gopd: 1.2.0 has-property-descriptors: 1.0.2 has-proto: 1.2.0 has-symbols: 1.1.0 hasown: 2.0.4 internal-slot: 1.1.0 is-array-buffer: 3.0.5 is-callable: 1.2.7 is-data-view: 1.0.2 is-negative-zero: 2.0.3 is-regex: 1.2.1 is-set: 2.0.3 is-shared-array-buffer: 1.0.4 is-string: 1.1.1 is-typed-array: 1.1.15 is-weakref: 1.1.1 math-intrinsics: 1.1.0 object-inspect: 1.13.4 object-keys: 1.1.1 object.assign: 4.1.7 own-keys: 1.0.1 regexp.prototype.flags: 1.5.4 safe-array-concat: 1.1.4 safe-push-apply: 1.0.0 safe-regex-test: 1.1.0 set-proto: 1.0.0 stop-iteration-iterator: 1.1.0 string.prototype.trim: 1.2.11 string.prototype.trimend: 1.0.10 string.prototype.trimstart: 1.0.8 typed-array-buffer: 1.0.3 typed-array-byte-length: 1.0.3 typed-array-byte-offset: 1.0.4 typed-array-length: 1.0.8 unbox-primitive: 1.1.0 which-typed-array: 1.1.22 es-define-property@1.0.1: {} es-errors@1.3.0: {} es-module-lexer@2.3.0: {} es-object-atoms@1.1.2: dependencies: es-errors: 1.3.0 es-set-tostringtag@2.1.0: dependencies: es-errors: 1.3.0 get-intrinsic: 1.3.0 has-tostringtag: 1.0.2 hasown: 2.0.4 es-shim-unscopables@1.1.0: dependencies: hasown: 2.0.4 es-to-primitive@1.3.4: dependencies: es-abstract-get: 1.0.0 es-define-property: 1.0.1 es-errors: 1.3.0 is-callable: 1.2.7 is-date-object: 1.1.0 is-symbol: 1.1.1 escalade@3.2.0: {} escape-string-regexp@1.0.5: {} escape-string-regexp@4.0.0: {} escape-string-regexp@5.0.0: {} eslint-config-decent@4.2.38(@stylistic/eslint-plugin@5.10.0(eslint@10.6.0))(@typescript-eslint/eslint-plugin@8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0)(typescript@7.0.2))(@typescript-eslint/utils@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint-plugin-jsdoc@62.9.0(eslint@10.6.0))(eslint-plugin-security@4.0.1)(eslint-plugin-unicorn@64.0.0(eslint@10.6.0))(eslint@10.6.0)(oxlint-plugin-eslint@1.73.0)(oxlint-tsgolint@0.24.0)(oxlint@1.72.0(oxlint-tsgolint@0.24.0)(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)))(prettier@3.9.5)(typescript@7.0.2): dependencies: '@eslint/compat': 2.0.5(eslint@10.6.0) '@eslint/js': 10.0.1(eslint@10.6.0) '@next/eslint-plugin-next': 16.2.4 eslint: 10.6.0 eslint-config-prettier: 10.1.8(eslint@10.6.0) eslint-plugin-import-x: 4.16.2(@typescript-eslint/utils@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0) eslint-plugin-jsx-a11y: 6.10.2(eslint@10.6.0) eslint-plugin-prettier: 5.5.5(eslint-config-prettier@10.1.8(eslint@10.6.0))(eslint@10.6.0)(prettier@3.9.5) eslint-plugin-promise: 7.2.1(eslint@10.6.0) eslint-plugin-react-hooks: 7.1.1(eslint@10.6.0) globals: 17.5.0 typescript: 7.0.2 typescript-eslint: 8.58.2(eslint@10.6.0)(typescript@7.0.2) optionalDependencies: '@stylistic/eslint-plugin': 5.10.0(eslint@10.6.0) '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0)(typescript@7.0.2) eslint-plugin-jsdoc: 62.9.0(eslint@10.6.0) eslint-plugin-security: 4.0.1 eslint-plugin-unicorn: 64.0.0(eslint@10.6.0) oxlint: 1.72.0(oxlint-tsgolint@0.24.0)(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)) oxlint-plugin-eslint: 1.73.0 oxlint-tsgolint: 0.24.0 transitivePeerDependencies: - '@types/eslint' - '@typescript-eslint/utils' - eslint-import-resolver-node - prettier - supports-color eslint-config-prettier@10.1.8(eslint@10.6.0): dependencies: eslint: 10.6.0 eslint-import-context@0.1.9(unrs-resolver@1.12.2): dependencies: get-tsconfig: 4.14.0 stable-hash-x: 0.2.0 optionalDependencies: unrs-resolver: 1.12.2 eslint-plugin-import-x@4.16.2(@typescript-eslint/utils@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0): dependencies: '@package-json/types': 0.0.12 '@typescript-eslint/types': 8.63.0 comment-parser: 1.4.7 debug: 4.4.3 eslint: 10.6.0 eslint-import-context: 0.1.9(unrs-resolver@1.12.2) is-glob: 4.0.3 minimatch: 10.2.5 semver: 7.8.5 stable-hash-x: 0.2.0 unrs-resolver: 1.12.2 optionalDependencies: '@typescript-eslint/utils': 8.58.2(eslint@10.6.0)(typescript@7.0.2) transitivePeerDependencies: - supports-color eslint-plugin-jsdoc@62.9.0(eslint@10.6.0): dependencies: '@es-joy/jsdoccomment': 0.86.0 '@es-joy/resolve.exports': 1.2.0 are-docs-informative: 0.0.2 comment-parser: 1.4.6 debug: 4.4.3 escape-string-regexp: 4.0.0 eslint: 10.6.0 espree: 11.2.0 esquery: 1.7.0 html-entities: 2.6.0 object-deep-merge: 2.0.1 parse-imports-exports: 0.2.4 semver: 7.8.5 spdx-expression-parse: 4.0.0 to-valid-identifier: 1.0.0 transitivePeerDependencies: - supports-color eslint-plugin-jsx-a11y@6.10.2(eslint@10.6.0): dependencies: aria-query: 5.3.2 array-includes: 3.1.9 array.prototype.flatmap: 1.3.3 ast-types-flow: 0.0.8 axe-core: 4.12.1 axobject-query: 4.1.0 damerau-levenshtein: 1.0.8 emoji-regex: 9.2.2 eslint: 10.6.0 hasown: 2.0.4 jsx-ast-utils: 3.3.5 language-tags: 1.0.9 minimatch: 3.1.5 object.fromentries: 2.0.8 safe-regex-test: 1.1.0 string.prototype.includes: 2.0.1 eslint-plugin-prettier@5.5.5(eslint-config-prettier@10.1.8(eslint@10.6.0))(eslint@10.6.0)(prettier@3.9.5): dependencies: eslint: 10.6.0 prettier: 3.9.5 prettier-linter-helpers: 1.0.1 synckit: 0.11.13 optionalDependencies: eslint-config-prettier: 10.1.8(eslint@10.6.0) eslint-plugin-promise@7.2.1(eslint@10.6.0): dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) eslint: 10.6.0 eslint-plugin-react-hooks@7.1.1(eslint@10.6.0): dependencies: '@babel/core': 7.29.7 '@babel/parser': 7.29.7 eslint: 10.6.0 hermes-parser: 0.25.1 zod: 4.4.3 zod-validation-error: 4.0.2(zod@4.4.3) transitivePeerDependencies: - supports-color eslint-plugin-security@4.0.1: dependencies: safe-regex: 2.1.1 eslint-plugin-unicorn@64.0.0(eslint@10.6.0): dependencies: '@babel/helper-validator-identifier': 7.29.7 '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) change-case: 5.4.4 ci-info: 4.4.0 clean-regexp: 1.0.0 core-js-compat: 3.49.0 eslint: 10.6.0 find-up-simple: 1.0.1 globals: 17.7.0 indent-string: 5.0.0 is-builtin-module: 5.0.0 jsesc: 3.1.0 pluralize: 8.0.0 regexp-tree: 0.1.27 regjsparser: 0.13.2 semver: 7.8.5 strip-indent: 4.1.1 eslint-scope@9.1.2: dependencies: '@types/esrecurse': 4.3.1 '@types/estree': 1.0.9 esrecurse: 4.3.0 estraverse: 5.3.0 eslint-visitor-keys@3.4.3: {} eslint-visitor-keys@4.2.1: {} eslint-visitor-keys@5.0.1: {} eslint@10.6.0: dependencies: '@eslint-community/eslint-utils': 4.9.1(eslint@10.6.0) '@eslint-community/regexpp': 4.12.2 '@eslint/config-array': 0.23.5 '@eslint/config-helpers': 0.6.0 '@eslint/core': 1.2.1 '@eslint/plugin-kit': 0.7.2 '@humanfs/node': 0.16.8 '@humanwhocodes/module-importer': 1.0.1 '@humanwhocodes/retry': 0.4.3 '@types/estree': 1.0.9 ajv: 6.15.0 cross-spawn: 7.0.6 debug: 4.4.3 escape-string-regexp: 4.0.0 eslint-scope: 9.1.2 eslint-visitor-keys: 5.0.1 espree: 11.2.0 esquery: 1.7.0 esutils: 2.0.3 fast-deep-equal: 3.1.3 file-entry-cache: 8.0.0 find-up: 5.0.0 glob-parent: 6.0.2 ignore: 5.3.2 imurmurhash: 0.1.4 is-glob: 4.0.3 json-stable-stringify-without-jsonify: 1.0.1 minimatch: 10.2.5 natural-compare: 1.4.0 optionator: 0.9.4 transitivePeerDependencies: - supports-color espree@10.4.0: dependencies: acorn: 8.17.0 acorn-jsx: 5.3.2(acorn@8.17.0) eslint-visitor-keys: 4.2.1 espree@11.2.0: dependencies: acorn: 8.17.0 acorn-jsx: 5.3.2(acorn@8.17.0) eslint-visitor-keys: 5.0.1 esquery@1.7.0: dependencies: estraverse: 5.3.0 esrecurse@4.3.0: dependencies: estraverse: 5.3.0 estraverse@5.3.0: {} estree-walker@3.0.3: dependencies: '@types/estree': 1.0.9 esutils@2.0.3: {} eventemitter3@5.0.4: {} execa@5.1.1: dependencies: cross-spawn: 7.0.6 get-stream: 6.0.1 human-signals: 2.1.0 is-stream: 2.0.1 merge-stream: 2.0.0 npm-run-path: 4.0.1 onetime: 5.1.2 signal-exit: 3.0.7 strip-final-newline: 2.0.0 execa@8.0.1: dependencies: cross-spawn: 7.0.6 get-stream: 8.0.1 human-signals: 5.0.0 is-stream: 3.0.0 merge-stream: 2.0.0 npm-run-path: 5.3.0 onetime: 6.0.0 signal-exit: 4.1.0 strip-final-newline: 3.0.0 execa@9.6.1: dependencies: '@sindresorhus/merge-streams': 4.0.0 cross-spawn: 7.0.6 figures: 6.1.0 get-stream: 9.0.1 human-signals: 8.0.1 is-plain-obj: 4.1.0 is-stream: 4.0.1 npm-run-path: 6.0.0 pretty-ms: 9.3.0 signal-exit: 4.1.0 strip-final-newline: 4.0.0 yoctocolors: 2.1.2 expect-type@1.4.0: {} fast-deep-equal@3.1.3: {} fast-diff@1.3.0: {} fast-glob@3.3.1: dependencies: '@nodelib/fs.stat': 2.0.5 '@nodelib/fs.walk': 1.2.8 glob-parent: 5.1.2 merge2: 1.4.1 micromatch: 4.0.8 fast-json-stable-stringify@2.1.0: {} fast-levenshtein@2.0.6: {} fastq@1.20.1: dependencies: reusify: 1.1.0 fdir@6.5.0(picomatch@4.0.5): optionalDependencies: picomatch: 4.0.5 figures@2.0.0: dependencies: escape-string-regexp: 1.0.5 figures@6.1.0: dependencies: is-unicode-supported: 2.1.0 file-entry-cache@8.0.0: dependencies: flat-cache: 4.0.1 fill-range@7.1.1: dependencies: to-regex-range: 5.0.1 find-up-simple@1.0.1: {} find-up@2.1.0: dependencies: locate-path: 2.0.0 find-up@5.0.0: dependencies: locate-path: 6.0.0 path-exists: 4.0.0 find-versions@6.0.0: dependencies: semver-regex: 4.0.5 super-regex: 1.1.0 flat-cache@4.0.1: dependencies: flatted: 3.4.2 keyv: 4.5.4 flatted@3.4.2: {} for-each@0.3.5: dependencies: is-callable: 1.2.7 fs-extra@11.3.6: dependencies: graceful-fs: 4.2.11 jsonfile: 6.2.1 universalify: 2.0.1 fsevents@2.3.3: optional: true function-bind@1.1.2: {} function-timeout@1.0.2: {} function.prototype.name@1.2.0: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 es-define-property: 1.0.1 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 hasown: 2.0.4 is-callable: 1.2.7 is-document.all: 1.0.0 functions-have-names@1.2.3: {} generator-function@2.0.1: {} gensync@1.0.0-beta.2: {} get-caller-file@2.0.5: {} get-east-asian-width@1.6.0: {} get-intrinsic@1.3.0: dependencies: call-bind-apply-helpers: 1.0.2 es-define-property: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.2 function-bind: 1.1.2 get-proto: 1.0.1 gopd: 1.2.0 has-symbols: 1.1.0 hasown: 2.0.4 math-intrinsics: 1.1.0 get-proto@1.0.1: dependencies: dunder-proto: 1.0.1 es-object-atoms: 1.1.2 get-stream@6.0.1: {} get-stream@8.0.1: {} get-stream@9.0.1: dependencies: '@sec-ant/readable-stream': 0.4.1 is-stream: 4.0.1 get-symbol-description@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 get-tsconfig@4.14.0: dependencies: resolve-pkg-maps: 1.0.0 git-log-parser@1.2.1: dependencies: argv-formatter: 1.0.0 spawn-error-forwarder: 1.0.0 split2: 1.0.0 stream-combiner2: 1.1.1 through2: 2.0.5 traverse: 0.6.8 glob-parent@5.1.2: dependencies: is-glob: 4.0.3 glob-parent@6.0.2: dependencies: is-glob: 4.0.3 globals@17.5.0: {} globals@17.7.0: {} globalthis@1.0.4: dependencies: define-properties: 1.2.1 gopd: 1.2.0 gopd@1.2.0: {} graceful-fs@4.2.10: {} graceful-fs@4.2.11: {} handlebars@4.7.9: dependencies: minimist: 1.2.8 neo-async: 2.6.2 source-map: 0.6.1 wordwrap: 1.0.0 optionalDependencies: uglify-js: 3.19.3 has-bigints@1.1.0: {} has-flag@3.0.0: {} has-flag@4.0.0: {} has-property-descriptors@1.0.2: dependencies: es-define-property: 1.0.1 has-proto@1.2.0: dependencies: dunder-proto: 1.0.1 has-symbols@1.1.0: {} has-tostringtag@1.0.2: dependencies: has-symbols: 1.1.0 hasown@2.0.4: dependencies: function-bind: 1.1.2 hermes-estree@0.25.1: {} hermes-parser@0.25.1: dependencies: hermes-estree: 0.25.1 highlight.js@10.7.3: {} hook-std@4.0.0: {} hosted-git-info@7.0.2: dependencies: lru-cache: 10.4.3 hosted-git-info@9.0.3: dependencies: lru-cache: 11.5.2 html-entities@2.6.0: {} http-proxy-agent@9.1.0: dependencies: agent-base: 9.0.0 debug: 4.4.3 proxy-agent-negotiate: 1.1.0 transitivePeerDependencies: - kerberos - supports-color https-proxy-agent@9.1.0: dependencies: agent-base: 9.0.0 debug: 4.4.3 proxy-agent-negotiate: 1.1.0 transitivePeerDependencies: - kerberos - supports-color human-signals@2.1.0: {} human-signals@5.0.0: {} human-signals@8.0.1: {} husky@9.1.7: {} ignore@5.3.2: {} ignore@7.0.6: {} import-fresh@3.3.1: dependencies: parent-module: 1.0.1 resolve-from: 4.0.0 import-from-esm@2.0.0: dependencies: debug: 4.4.3 import-meta-resolve: 4.2.0 transitivePeerDependencies: - supports-color import-meta-resolve@4.2.0: {} imurmurhash@0.1.4: {} indent-string@4.0.0: {} indent-string@5.0.0: {} index-to-position@1.2.0: {} inherits@2.0.4: {} ini@1.3.8: {} ini@4.1.3: {} internal-slot@1.1.0: dependencies: es-errors: 1.3.0 hasown: 2.0.4 side-channel: 1.1.1 is-alphabetical@2.0.1: {} is-alphanumerical@2.0.1: dependencies: is-alphabetical: 2.0.1 is-decimal: 2.0.1 is-array-buffer@3.0.5: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 is-arrayish@0.2.1: {} is-async-function@2.1.1: dependencies: async-function: 1.0.0 call-bound: 1.0.4 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 is-bigint@1.1.0: dependencies: has-bigints: 1.1.0 is-boolean-object@1.2.2: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 is-builtin-module@5.0.0: dependencies: builtin-modules: 5.3.0 is-callable@1.2.7: {} is-data-view@1.0.2: dependencies: call-bound: 1.0.4 get-intrinsic: 1.3.0 is-typed-array: 1.1.15 is-date-object@1.1.0: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 is-decimal@2.0.1: {} is-document.all@1.0.0: dependencies: call-bound: 1.0.4 is-extglob@2.1.1: {} is-finalizationregistry@1.1.1: dependencies: call-bound: 1.0.4 is-fullwidth-code-point@3.0.0: {} is-fullwidth-code-point@5.1.0: dependencies: get-east-asian-width: 1.6.0 is-generator-function@1.1.2: dependencies: call-bound: 1.0.4 generator-function: 2.0.1 get-proto: 1.0.1 has-tostringtag: 1.0.2 safe-regex-test: 1.1.0 is-glob@4.0.3: dependencies: is-extglob: 2.1.1 is-hexadecimal@2.0.1: {} is-map@2.0.3: {} is-negative-zero@2.0.3: {} is-number-object@1.1.1: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 is-number@7.0.0: {} is-obj@2.0.0: {} is-plain-obj@4.1.0: {} is-regex@1.2.1: dependencies: call-bound: 1.0.4 gopd: 1.2.0 has-tostringtag: 1.0.2 hasown: 2.0.4 is-set@2.0.3: {} is-shared-array-buffer@1.0.4: dependencies: call-bound: 1.0.4 is-stream@2.0.1: {} is-stream@3.0.0: {} is-stream@4.0.1: {} is-string@1.1.1: dependencies: call-bound: 1.0.4 has-tostringtag: 1.0.2 is-symbol@1.1.1: dependencies: call-bound: 1.0.4 has-symbols: 1.1.0 safe-regex-test: 1.1.0 is-typed-array@1.1.15: dependencies: which-typed-array: 1.1.22 is-unicode-supported@2.1.0: {} is-weakmap@2.0.2: {} is-weakref@1.1.1: dependencies: call-bound: 1.0.4 is-weakset@2.0.4: dependencies: call-bound: 1.0.4 get-intrinsic: 1.3.0 isarray@1.0.0: {} isarray@2.0.5: {} isexe@2.0.0: {} isexe@4.0.0: {} issue-parser@7.0.2: dependencies: lodash.capitalize: 4.2.1 lodash.escaperegexp: 4.1.2 lodash.isplainobject: 4.0.6 lodash.isstring: 4.0.1 lodash.uniqby: 4.7.0 java-properties@1.0.2: {} js-tokens@4.0.0: {} js-yaml@4.2.0: dependencies: argparse: 2.0.1 js-yaml@4.3.0: dependencies: argparse: 2.0.1 jsdoc-type-pratt-parser@7.2.0: {} jsesc@3.1.0: {} json-buffer@3.0.1: {} json-parse-better-errors@1.0.2: {} json-parse-even-better-errors@2.3.1: {} json-parse-even-better-errors@6.0.0: {} json-schema-traverse@0.4.1: {} json-stable-stringify-without-jsonify@1.0.1: {} json-with-bigint@3.5.8: {} json5@2.2.3: {} jsonc-parser@3.3.1: {} jsonfile@6.2.1: dependencies: universalify: 2.0.1 optionalDependencies: graceful-fs: 4.2.11 jsonpointer@5.0.1: {} jsx-ast-utils@3.3.5: dependencies: array-includes: 3.1.9 array.prototype.flat: 1.3.3 object.assign: 4.1.7 object.values: 1.2.1 katex@0.16.47: dependencies: commander: 8.3.0 keyv@4.5.4: dependencies: json-buffer: 3.0.1 language-subtag-registry@0.3.23: {} language-tags@1.0.9: dependencies: language-subtag-registry: 0.3.23 levn@0.4.1: dependencies: prelude-ls: 1.2.1 type-check: 0.4.0 lightningcss-android-arm64@1.32.0: optional: true lightningcss-darwin-arm64@1.32.0: optional: true lightningcss-darwin-x64@1.32.0: optional: true lightningcss-freebsd-x64@1.32.0: optional: true lightningcss-linux-arm-gnueabihf@1.32.0: optional: true lightningcss-linux-arm64-gnu@1.32.0: optional: true lightningcss-linux-arm64-musl@1.32.0: optional: true lightningcss-linux-x64-gnu@1.32.0: optional: true lightningcss-linux-x64-musl@1.32.0: optional: true lightningcss-win32-arm64-msvc@1.32.0: optional: true lightningcss-win32-x64-msvc@1.32.0: optional: true lightningcss@1.32.0: dependencies: detect-libc: 2.1.2 optionalDependencies: lightningcss-android-arm64: 1.32.0 lightningcss-darwin-arm64: 1.32.0 lightningcss-darwin-x64: 1.32.0 lightningcss-freebsd-x64: 1.32.0 lightningcss-linux-arm-gnueabihf: 1.32.0 lightningcss-linux-arm64-gnu: 1.32.0 lightningcss-linux-arm64-musl: 1.32.0 lightningcss-linux-x64-gnu: 1.32.0 lightningcss-linux-x64-musl: 1.32.0 lightningcss-win32-arm64-msvc: 1.32.0 lightningcss-win32-x64-msvc: 1.32.0 lines-and-columns@1.2.4: {} linkify-it@5.0.2: dependencies: uc.micro: 2.1.0 lint-staged@17.0.8: dependencies: listr2: 10.2.2 picomatch: 4.0.5 string-argv: 0.3.2 tinyexec: 1.2.4 optionalDependencies: yaml: 2.9.0 listr2@10.2.2: dependencies: cli-truncate: 5.2.0 eventemitter3: 5.0.4 log-update: 6.1.0 rfdc: 1.4.1 wrap-ansi: 10.0.0 load-json-file@4.0.0: dependencies: graceful-fs: 4.2.11 parse-json: 4.0.0 pify: 3.0.0 strip-bom: 3.0.0 locate-path@2.0.0: dependencies: p-locate: 2.0.0 path-exists: 3.0.0 locate-path@6.0.0: dependencies: p-locate: 5.0.0 lodash-es@4.18.1: {} lodash.capitalize@4.2.1: {} lodash.escaperegexp@4.1.2: {} lodash.isplainobject@4.0.6: {} lodash.isstring@4.0.1: {} lodash.uniqby@4.7.0: {} lodash@4.18.1: {} log-update@6.1.0: dependencies: ansi-escapes: 7.3.0 cli-cursor: 5.0.0 slice-ansi: 7.1.2 strip-ansi: 7.2.0 wrap-ansi: 9.0.2 lru-cache@10.4.3: {} lru-cache@11.5.2: {} lru-cache@5.1.1: dependencies: yallist: 3.1.1 lz-string@1.5.0: {} magic-string@0.30.21: dependencies: '@jridgewell/sourcemap-codec': 1.5.5 make-asynchronous@1.1.0: dependencies: p-event: 6.0.1 type-fest: 4.41.0 web-worker: 1.5.0 markdown-it@14.2.0: dependencies: argparse: 2.0.1 entities: 4.5.0 linkify-it: 5.0.2 mdurl: 2.0.0 punycode.js: 2.3.1 uc.micro: 2.1.0 markdownlint-cli@0.49.0: dependencies: commander: 15.0.0 deep-extend: 0.6.0 ignore: 7.0.6 js-yaml: 4.2.0 jsonc-parser: 3.3.1 jsonpointer: 5.0.1 markdown-it: 14.2.0 markdownlint: 0.41.0 minimatch: 10.2.5 run-con: 1.3.2 smol-toml: 1.6.1 tinyglobby: 0.2.17 transitivePeerDependencies: - supports-color markdownlint@0.41.0: dependencies: micromark: 4.0.2 micromark-core-commonmark: 2.0.3 micromark-extension-directive: 4.0.0 micromark-extension-gfm-autolink-literal: 2.1.0 micromark-extension-gfm-footnote: 2.1.0 micromark-extension-gfm-table: 2.1.1 micromark-extension-math: 3.1.0 micromark-util-types: 2.0.2 string-width: 8.2.1 transitivePeerDependencies: - supports-color marked-terminal@7.3.0(marked@15.0.12): dependencies: ansi-escapes: 7.3.0 ansi-regex: 6.2.2 chalk: 5.6.2 cli-highlight: 2.1.11 cli-table3: 0.6.5 marked: 15.0.12 node-emoji: 2.2.0 supports-hyperlinks: 3.2.0 marked@15.0.12: {} math-intrinsics@1.1.0: {} mdurl@2.0.0: {} memorystream@0.3.1: {} meow@13.2.0: {} merge-stream@2.0.0: {} merge2@1.4.1: {} micromark-core-commonmark@2.0.3: dependencies: decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-factory-destination: 2.0.1 micromark-factory-label: 2.0.1 micromark-factory-space: 2.0.1 micromark-factory-title: 2.0.1 micromark-factory-whitespace: 2.0.1 micromark-util-character: 2.1.1 micromark-util-chunked: 2.0.1 micromark-util-classify-character: 2.0.1 micromark-util-html-tag-name: 2.0.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 micromark-util-subtokenize: 2.1.0 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-directive@4.0.0: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.1 micromark-factory-whitespace: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 parse-entities: 4.0.2 micromark-extension-gfm-autolink-literal@2.1.0: dependencies: micromark-util-character: 2.1.1 micromark-util-sanitize-uri: 2.0.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-gfm-footnote@2.1.0: dependencies: devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-sanitize-uri: 2.0.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-gfm-table@2.1.1: dependencies: devlop: 1.1.0 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-extension-math@3.1.0: dependencies: '@types/katex': 0.16.8 devlop: 1.1.0 katex: 0.16.47 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-factory-destination@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-factory-label@2.0.1: dependencies: devlop: 1.1.0 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-factory-space@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-types: 2.0.2 micromark-factory-title@2.0.1: dependencies: micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-factory-whitespace@2.0.1: dependencies: micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-util-character@2.1.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-util-chunked@2.0.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-classify-character@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-util-combine-extensions@2.0.1: dependencies: micromark-util-chunked: 2.0.1 micromark-util-types: 2.0.2 micromark-util-decode-numeric-character-reference@2.0.2: dependencies: micromark-util-symbol: 2.0.1 micromark-util-encode@2.0.1: {} micromark-util-html-tag-name@2.0.1: {} micromark-util-normalize-identifier@2.0.1: dependencies: micromark-util-symbol: 2.0.1 micromark-util-resolve-all@2.0.1: dependencies: micromark-util-types: 2.0.2 micromark-util-sanitize-uri@2.0.1: dependencies: micromark-util-character: 2.1.1 micromark-util-encode: 2.0.1 micromark-util-symbol: 2.0.1 micromark-util-subtokenize@2.1.0: dependencies: devlop: 1.1.0 micromark-util-chunked: 2.0.1 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 micromark-util-symbol@2.0.1: {} micromark-util-types@2.0.2: {} micromark@4.0.2: dependencies: '@types/debug': 4.1.13 debug: 4.4.3 decode-named-character-reference: 1.3.0 devlop: 1.1.0 micromark-core-commonmark: 2.0.3 micromark-factory-space: 2.0.1 micromark-util-character: 2.1.1 micromark-util-chunked: 2.0.1 micromark-util-combine-extensions: 2.0.1 micromark-util-decode-numeric-character-reference: 2.0.2 micromark-util-encode: 2.0.1 micromark-util-normalize-identifier: 2.0.1 micromark-util-resolve-all: 2.0.1 micromark-util-sanitize-uri: 2.0.1 micromark-util-subtokenize: 2.1.0 micromark-util-symbol: 2.0.1 micromark-util-types: 2.0.2 transitivePeerDependencies: - supports-color micromatch@4.0.8: dependencies: braces: 3.0.3 picomatch: 2.3.2 mime@4.1.0: {} mimic-fn@2.1.0: {} mimic-fn@4.0.0: {} mimic-function@5.0.1: {} minimatch@10.2.5: dependencies: brace-expansion: 5.0.7 minimatch@3.1.5: dependencies: brace-expansion: 1.1.16 minimist@1.2.8: {} mrmime@2.0.1: {} ms@2.1.3: {} mz@2.7.0: dependencies: any-promise: 1.3.0 object-assign: 4.1.1 thenify-all: 1.6.0 nanoid@3.3.15: {} napi-postinstall@0.3.4: {} natural-compare@1.4.0: {} neo-async@2.6.2: {} nerf-dart@1.0.0: {} node-emoji@2.2.0: dependencies: '@sindresorhus/is': 4.6.0 char-regex: 1.0.2 emojilib: 2.4.0 skin-tone: 2.0.0 node-forge@1.4.0: {} node-releases@2.0.51: {} normalize-package-data@6.0.2: dependencies: hosted-git-info: 7.0.2 semver: 7.8.5 validate-npm-package-license: 3.0.4 normalize-package-data@8.0.0: dependencies: hosted-git-info: 9.0.3 semver: 7.8.5 validate-npm-package-license: 3.0.4 normalize-url@9.0.1: {} npm-normalize-package-bin@6.0.0: {} npm-run-all2@9.0.2: dependencies: ansi-styles: 6.2.3 cross-spawn: 7.0.6 memorystream: 0.3.1 picomatch: 4.0.5 pidtree: 1.0.0 read-package-json-fast: 6.0.0 shell-quote: 1.9.0 which: 7.0.0 npm-run-path@4.0.1: dependencies: path-key: 3.1.1 npm-run-path@5.3.0: dependencies: path-key: 4.0.0 npm-run-path@6.0.0: dependencies: path-key: 4.0.0 unicorn-magic: 0.3.0 npm@11.18.0: {} object-assign@4.1.1: {} object-deep-merge@2.0.1: {} object-inspect@1.13.4: {} object-keys@1.1.1: {} object.assign@4.1.7: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.2 has-symbols: 1.1.0 object-keys: 1.1.1 object.fromentries@2.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-object-atoms: 1.1.2 object.values@1.2.1: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.2 obug@2.1.3: {} onetime@5.1.2: dependencies: mimic-fn: 2.1.0 onetime@6.0.0: dependencies: mimic-fn: 4.0.0 onetime@7.0.0: dependencies: mimic-function: 5.0.1 optionator@0.9.4: dependencies: deep-is: 0.1.4 fast-levenshtein: 2.0.6 levn: 0.4.1 prelude-ls: 1.2.1 type-check: 0.4.0 word-wrap: 1.2.5 own-keys@1.0.1: dependencies: get-intrinsic: 1.3.0 object-keys: 1.1.1 safe-push-apply: 1.0.0 oxfmt@0.57.0(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)): dependencies: tinypool: 2.1.0 optionalDependencies: '@oxfmt/binding-android-arm-eabi': 0.57.0 '@oxfmt/binding-android-arm64': 0.57.0 '@oxfmt/binding-darwin-arm64': 0.57.0 '@oxfmt/binding-darwin-x64': 0.57.0 '@oxfmt/binding-freebsd-x64': 0.57.0 '@oxfmt/binding-linux-arm-gnueabihf': 0.57.0 '@oxfmt/binding-linux-arm-musleabihf': 0.57.0 '@oxfmt/binding-linux-arm64-gnu': 0.57.0 '@oxfmt/binding-linux-arm64-musl': 0.57.0 '@oxfmt/binding-linux-ppc64-gnu': 0.57.0 '@oxfmt/binding-linux-riscv64-gnu': 0.57.0 '@oxfmt/binding-linux-riscv64-musl': 0.57.0 '@oxfmt/binding-linux-s390x-gnu': 0.57.0 '@oxfmt/binding-linux-x64-gnu': 0.57.0 '@oxfmt/binding-linux-x64-musl': 0.57.0 '@oxfmt/binding-openharmony-arm64': 0.57.0 '@oxfmt/binding-win32-arm64-msvc': 0.57.0 '@oxfmt/binding-win32-ia32-msvc': 0.57.0 '@oxfmt/binding-win32-x64-msvc': 0.57.0 vite-plus: 0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0) oxlint-plugin-eslint@1.73.0: {} oxlint-tsgolint@0.24.0: optionalDependencies: '@oxlint-tsgolint/darwin-arm64': 0.24.0 '@oxlint-tsgolint/darwin-x64': 0.24.0 '@oxlint-tsgolint/linux-arm64': 0.24.0 '@oxlint-tsgolint/linux-x64': 0.24.0 '@oxlint-tsgolint/win32-arm64': 0.24.0 '@oxlint-tsgolint/win32-x64': 0.24.0 oxlint@1.72.0(oxlint-tsgolint@0.24.0)(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)): optionalDependencies: '@oxlint/binding-android-arm-eabi': 1.72.0 '@oxlint/binding-android-arm64': 1.72.0 '@oxlint/binding-darwin-arm64': 1.72.0 '@oxlint/binding-darwin-x64': 1.72.0 '@oxlint/binding-freebsd-x64': 1.72.0 '@oxlint/binding-linux-arm-gnueabihf': 1.72.0 '@oxlint/binding-linux-arm-musleabihf': 1.72.0 '@oxlint/binding-linux-arm64-gnu': 1.72.0 '@oxlint/binding-linux-arm64-musl': 1.72.0 '@oxlint/binding-linux-ppc64-gnu': 1.72.0 '@oxlint/binding-linux-riscv64-gnu': 1.72.0 '@oxlint/binding-linux-riscv64-musl': 1.72.0 '@oxlint/binding-linux-s390x-gnu': 1.72.0 '@oxlint/binding-linux-x64-gnu': 1.72.0 '@oxlint/binding-linux-x64-musl': 1.72.0 '@oxlint/binding-openharmony-arm64': 1.72.0 '@oxlint/binding-win32-arm64-msvc': 1.72.0 '@oxlint/binding-win32-ia32-msvc': 1.72.0 '@oxlint/binding-win32-x64-msvc': 1.72.0 oxlint-tsgolint: 0.24.0 vite-plus: 0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0) p-each-series@3.0.0: {} p-event@6.0.1: dependencies: p-timeout: 6.1.4 p-filter@4.1.0: dependencies: p-map: 7.0.5 p-limit@1.3.0: dependencies: p-try: 1.0.0 p-limit@3.1.0: dependencies: yocto-queue: 0.1.0 p-locate@2.0.0: dependencies: p-limit: 1.3.0 p-locate@5.0.0: dependencies: p-limit: 3.1.0 p-map@7.0.5: {} p-reduce@2.1.0: {} p-reduce@3.0.0: {} p-timeout@6.1.4: {} p-try@1.0.0: {} parent-module@1.0.1: dependencies: callsites: 3.1.0 parse-entities@4.0.2: dependencies: '@types/unist': 2.0.11 character-entities-legacy: 3.0.0 character-reference-invalid: 2.0.1 decode-named-character-reference: 1.3.0 is-alphanumerical: 2.0.1 is-decimal: 2.0.1 is-hexadecimal: 2.0.1 parse-imports-exports@0.2.4: dependencies: parse-statements: 1.0.11 parse-json@4.0.0: dependencies: error-ex: 1.3.4 json-parse-better-errors: 1.0.2 parse-json@5.2.0: dependencies: '@babel/code-frame': 7.29.7 error-ex: 1.3.4 json-parse-even-better-errors: 2.3.1 lines-and-columns: 1.2.4 parse-json@8.3.0: dependencies: '@babel/code-frame': 7.29.7 index-to-position: 1.2.0 type-fest: 4.41.0 parse-ms@4.0.0: {} parse-statements@1.0.11: {} parse5-htmlparser2-tree-adapter@6.0.1: dependencies: parse5: 6.0.1 parse5@5.1.1: {} parse5@6.0.1: {} path-exists@3.0.0: {} path-exists@4.0.0: {} path-key@3.1.1: {} path-key@4.0.0: {} path-type@4.0.0: {} pathe@2.0.3: {} picocolors@1.1.1: {} picomatch@2.3.2: {} picomatch@4.0.5: {} pidtree@1.0.0: {} pify@3.0.0: {} pinst@3.0.0: {} pkg-conf@2.1.0: dependencies: find-up: 2.1.0 load-json-file: 4.0.0 pluralize@8.0.0: {} pngjs@7.0.0: {} possible-typed-array-names@1.1.0: {} postcss@8.5.16: dependencies: nanoid: 3.3.15 picocolors: 1.1.1 source-map-js: 1.2.1 prelude-ls@1.2.1: {} prettier-linter-helpers@1.0.1: dependencies: fast-diff: 1.3.0 prettier@3.9.5: {} pretty-format@27.5.1: dependencies: ansi-regex: 5.0.1 ansi-styles: 5.2.0 react-is: 17.0.2 pretty-ms@9.3.0: dependencies: parse-ms: 4.0.0 process-nextick-args@2.0.1: {} proto-list@1.2.4: {} proxy-agent-negotiate@1.1.0: {} punycode.js@2.3.1: {} punycode@2.3.1: {} queue-microtask@1.2.3: {} rc@1.2.8: dependencies: deep-extend: 0.6.0 ini: 1.3.8 minimist: 1.2.8 strip-json-comments: 2.0.1 react-is@17.0.2: {} read-package-json-fast@6.0.0: dependencies: json-parse-even-better-errors: 6.0.0 npm-normalize-package-bin: 6.0.0 read-package-up@11.0.0: dependencies: find-up-simple: 1.0.1 read-pkg: 9.0.1 type-fest: 4.41.0 read-package-up@12.0.0: dependencies: find-up-simple: 1.0.1 read-pkg: 10.1.0 type-fest: 5.8.0 read-pkg@10.1.0: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 8.0.0 parse-json: 8.3.0 type-fest: 5.8.0 unicorn-magic: 0.4.0 read-pkg@9.0.1: dependencies: '@types/normalize-package-data': 2.4.4 normalize-package-data: 6.0.2 parse-json: 8.3.0 type-fest: 4.41.0 unicorn-magic: 0.1.0 readable-stream@2.3.8: dependencies: core-util-is: 1.0.3 inherits: 2.0.4 isarray: 1.0.0 process-nextick-args: 2.0.1 safe-buffer: 5.1.2 string_decoder: 1.1.1 util-deprecate: 1.0.2 reflect.getprototypeof@1.0.10: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 es-errors: 1.3.0 es-object-atoms: 1.1.2 get-intrinsic: 1.3.0 get-proto: 1.0.1 which-builtin-type: 1.2.1 regexp-tree@0.1.27: {} regexp.prototype.flags@1.5.4: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-errors: 1.3.0 get-proto: 1.0.1 gopd: 1.2.0 set-function-name: 2.0.2 registry-auth-token@5.1.1: dependencies: '@pnpm/npm-conf': 3.0.3 regjsparser@0.13.2: dependencies: jsesc: 3.1.0 require-directory@2.1.1: {} reserved-identifiers@1.2.0: {} resolve-from@4.0.0: {} resolve-from@5.0.0: {} resolve-pkg-maps@1.0.0: {} restore-cursor@5.1.0: dependencies: onetime: 7.0.0 signal-exit: 4.1.0 reusify@1.1.0: {} rfdc@1.4.1: {} run-con@1.3.2: dependencies: deep-extend: 0.6.0 ini: 4.1.3 minimist: 1.2.8 strip-json-comments: 3.1.1 run-parallel@1.2.0: dependencies: queue-microtask: 1.2.3 safe-array-concat@1.1.4: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 get-intrinsic: 1.3.0 has-symbols: 1.1.0 isarray: 2.0.5 safe-buffer@5.1.2: {} safe-push-apply@1.0.0: dependencies: es-errors: 1.3.0 isarray: 2.0.5 safe-regex-test@1.1.0: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-regex: 1.2.1 safe-regex@2.1.1: dependencies: regexp-tree: 0.1.27 semantic-release@25.0.5(typescript@7.0.2): dependencies: '@semantic-release/commit-analyzer': 13.0.1(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/error': 4.0.0 '@semantic-release/github': 12.0.9(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/npm': 13.1.5(semantic-release@25.0.5(typescript@7.0.2)) '@semantic-release/release-notes-generator': 14.1.1(semantic-release@25.0.5(typescript@7.0.2)) aggregate-error: 5.0.0 cosmiconfig: 9.0.2(typescript@7.0.2) debug: 4.4.3 env-ci: 11.2.0 execa: 9.6.1 figures: 6.1.0 find-versions: 6.0.0 get-stream: 6.0.1 git-log-parser: 1.2.1 hook-std: 4.0.0 hosted-git-info: 9.0.3 import-from-esm: 2.0.0 lodash-es: 4.18.1 marked: 15.0.12 marked-terminal: 7.3.0(marked@15.0.12) micromatch: 4.0.8 p-each-series: 3.0.0 p-reduce: 3.0.0 read-package-up: 12.0.0 resolve-from: 5.0.0 semver: 7.8.5 signale: 1.4.0 yargs: 18.0.0 transitivePeerDependencies: - kerberos - supports-color - typescript semver-regex@4.0.5: {} semver@6.3.1: {} semver@7.8.5: {} set-function-length@1.2.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 function-bind: 1.1.2 get-intrinsic: 1.3.0 gopd: 1.2.0 has-property-descriptors: 1.0.2 set-function-name@2.0.2: dependencies: define-data-property: 1.1.4 es-errors: 1.3.0 functions-have-names: 1.2.3 has-property-descriptors: 1.0.2 set-proto@1.0.0: dependencies: dunder-proto: 1.0.1 es-errors: 1.3.0 es-object-atoms: 1.1.2 shebang-command@2.0.0: dependencies: shebang-regex: 3.0.0 shebang-regex@3.0.0: {} shell-quote@1.9.0: {} side-channel-list@1.0.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 side-channel-map@1.0.1: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-weakmap@1.0.2: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 get-intrinsic: 1.3.0 object-inspect: 1.13.4 side-channel-map: 1.0.1 side-channel@1.1.1: dependencies: es-errors: 1.3.0 object-inspect: 1.13.4 side-channel-list: 1.0.1 side-channel-map: 1.0.1 side-channel-weakmap: 1.0.2 siginfo@2.0.0: {} signal-exit@3.0.7: {} signal-exit@4.1.0: {} signale@1.4.0: dependencies: chalk: 2.4.2 figures: 2.0.0 pkg-conf: 2.1.0 sirv@3.0.2: dependencies: '@polka/url': 1.0.0-next.29 mrmime: 2.0.1 totalist: 3.0.1 skin-tone@2.0.0: dependencies: unicode-emoji-modifier-base: 1.0.0 slice-ansi@7.1.2: dependencies: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 slice-ansi@8.0.0: dependencies: ansi-styles: 6.2.3 is-fullwidth-code-point: 5.1.0 smol-toml@1.6.1: {} source-map-js@1.2.1: {} source-map@0.6.1: {} spawn-error-forwarder@1.0.0: {} spdx-correct@3.2.0: dependencies: spdx-expression-parse: 3.0.1 spdx-license-ids: 3.0.23 spdx-exceptions@2.5.0: {} spdx-expression-parse@3.0.1: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.23 spdx-expression-parse@4.0.0: dependencies: spdx-exceptions: 2.5.0 spdx-license-ids: 3.0.23 spdx-license-ids@3.0.23: {} split2@1.0.0: dependencies: through2: 2.0.5 stable-hash-x@0.2.0: {} stackback@0.0.2: {} std-env@4.2.0: {} stop-iteration-iterator@1.1.0: dependencies: es-errors: 1.3.0 internal-slot: 1.1.0 stream-combiner2@1.1.1: dependencies: duplexer2: 0.1.4 readable-stream: 2.3.8 strict-event-emitter-types@2.0.0: {} string-argv@0.3.2: {} string-width@4.2.3: dependencies: emoji-regex: 8.0.0 is-fullwidth-code-point: 3.0.0 strip-ansi: 6.0.1 string-width@7.2.0: dependencies: emoji-regex: 10.6.0 get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string-width@8.2.1: dependencies: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string-width@8.2.2: dependencies: get-east-asian-width: 1.6.0 strip-ansi: 7.2.0 string.prototype.includes@2.0.1: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-abstract: 1.24.2 string.prototype.trim@1.2.11: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-data-property: 1.1.4 define-properties: 1.2.1 es-abstract: 1.24.2 es-object-atoms: 1.1.2 has-property-descriptors: 1.0.2 safe-regex-test: 1.1.0 string.prototype.trimend@1.0.10: dependencies: call-bind: 1.0.9 call-bound: 1.0.4 define-properties: 1.2.1 es-object-atoms: 1.1.2 string.prototype.trimstart@1.0.8: dependencies: call-bind: 1.0.9 define-properties: 1.2.1 es-object-atoms: 1.1.2 string_decoder@1.1.1: dependencies: safe-buffer: 5.1.2 strip-ansi@6.0.1: dependencies: ansi-regex: 5.0.1 strip-ansi@7.2.0: dependencies: ansi-regex: 6.2.2 strip-bom@3.0.0: {} strip-final-newline@2.0.0: {} strip-final-newline@3.0.0: {} strip-final-newline@4.0.0: {} strip-indent@4.1.1: {} strip-json-comments@2.0.1: {} strip-json-comments@3.1.1: {} super-regex@1.1.0: dependencies: function-timeout: 1.0.2 make-asynchronous: 1.1.0 time-span: 5.1.0 supports-color@5.5.0: dependencies: has-flag: 3.0.0 supports-color@7.2.0: dependencies: has-flag: 4.0.0 supports-hyperlinks@3.2.0: dependencies: has-flag: 4.0.0 supports-color: 7.2.0 synckit@0.11.13: dependencies: '@pkgr/core': 0.3.6 tagged-tag@1.0.0: {} temp-dir@3.0.0: {} tempy@3.2.0: dependencies: is-stream: 3.0.0 temp-dir: 3.0.0 type-fest: 2.19.0 unique-string: 3.0.0 thenify-all@1.6.0: dependencies: thenify: 3.3.1 thenify@3.3.1: dependencies: any-promise: 1.3.0 through2@2.0.5: dependencies: readable-stream: 2.3.8 xtend: 4.0.2 time-span@5.1.0: dependencies: convert-hrtime: 5.0.0 tinybench@2.9.0: {} tinyexec@1.2.4: {} tinyglobby@0.2.17: dependencies: fdir: 6.5.0(picomatch@4.0.5) picomatch: 4.0.5 tinypool@2.1.0: {} tinyrainbow@3.1.0: {} to-regex-range@5.0.1: dependencies: is-number: 7.0.0 to-valid-identifier@1.0.0: dependencies: '@sindresorhus/base62': 1.0.0 reserved-identifiers: 1.2.0 totalist@3.0.1: {} traverse@0.6.8: {} ts-api-utils@2.5.0(typescript@7.0.2): dependencies: typescript: 7.0.2 tslib@2.8.1: optional: true tunnel@0.0.6: {} type-check@0.4.0: dependencies: prelude-ls: 1.2.1 type-fest@1.4.0: {} type-fest@2.19.0: {} type-fest@4.41.0: {} type-fest@5.8.0: dependencies: tagged-tag: 1.0.0 typed-array-buffer@1.0.3: dependencies: call-bound: 1.0.4 es-errors: 1.3.0 is-typed-array: 1.1.15 typed-array-byte-length@1.0.3: dependencies: call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 typed-array-byte-offset@1.0.4: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 has-proto: 1.2.0 is-typed-array: 1.1.15 reflect.getprototypeof: 1.0.10 typed-array-length@1.0.8: dependencies: call-bind: 1.0.9 for-each: 0.3.5 gopd: 1.2.0 is-typed-array: 1.1.15 possible-typed-array-names: 1.1.0 reflect.getprototypeof: 1.0.10 typescript-eslint@8.58.2(eslint@10.6.0)(typescript@7.0.2): dependencies: '@typescript-eslint/eslint-plugin': 8.58.2(@typescript-eslint/parser@8.58.2(eslint@10.6.0)(typescript@7.0.2))(eslint@10.6.0)(typescript@7.0.2) '@typescript-eslint/parser': 8.58.2(eslint@10.6.0)(typescript@7.0.2) '@typescript-eslint/typescript-estree': 8.58.2(typescript@7.0.2) '@typescript-eslint/utils': 8.58.2(eslint@10.6.0)(typescript@7.0.2) eslint: 10.6.0 typescript: 7.0.2 transitivePeerDependencies: - supports-color typescript@7.0.2: optionalDependencies: '@typescript/typescript-aix-ppc64': 7.0.2 '@typescript/typescript-darwin-arm64': 7.0.2 '@typescript/typescript-darwin-x64': 7.0.2 '@typescript/typescript-freebsd-arm64': 7.0.2 '@typescript/typescript-freebsd-x64': 7.0.2 '@typescript/typescript-linux-arm': 7.0.2 '@typescript/typescript-linux-arm64': 7.0.2 '@typescript/typescript-linux-loong64': 7.0.2 '@typescript/typescript-linux-mips64el': 7.0.2 '@typescript/typescript-linux-ppc64': 7.0.2 '@typescript/typescript-linux-riscv64': 7.0.2 '@typescript/typescript-linux-s390x': 7.0.2 '@typescript/typescript-linux-x64': 7.0.2 '@typescript/typescript-netbsd-arm64': 7.0.2 '@typescript/typescript-netbsd-x64': 7.0.2 '@typescript/typescript-openbsd-arm64': 7.0.2 '@typescript/typescript-openbsd-x64': 7.0.2 '@typescript/typescript-sunos-x64': 7.0.2 '@typescript/typescript-win32-arm64': 7.0.2 '@typescript/typescript-win32-x64': 7.0.2 uc.micro@2.1.0: {} uglify-js@3.19.3: optional: true unbox-primitive@1.1.0: dependencies: call-bound: 1.0.4 has-bigints: 1.1.0 has-symbols: 1.1.0 which-boxed-primitive: 1.1.1 undici-types@8.3.0: {} undici@6.27.0: {} undici@7.28.0: {} unicode-emoji-modifier-base@1.0.0: {} unicorn-magic@0.1.0: {} unicorn-magic@0.3.0: {} unicorn-magic@0.4.0: {} unique-string@3.0.0: dependencies: crypto-random-string: 4.0.0 universal-user-agent@7.0.3: {} universalify@2.0.1: {} unrs-resolver@1.12.2: dependencies: napi-postinstall: 0.3.4 optionalDependencies: '@unrs/resolver-binding-android-arm-eabi': 1.12.2 '@unrs/resolver-binding-android-arm64': 1.12.2 '@unrs/resolver-binding-darwin-arm64': 1.12.2 '@unrs/resolver-binding-darwin-x64': 1.12.2 '@unrs/resolver-binding-freebsd-x64': 1.12.2 '@unrs/resolver-binding-linux-arm-gnueabihf': 1.12.2 '@unrs/resolver-binding-linux-arm-musleabihf': 1.12.2 '@unrs/resolver-binding-linux-arm64-gnu': 1.12.2 '@unrs/resolver-binding-linux-arm64-musl': 1.12.2 '@unrs/resolver-binding-linux-loong64-gnu': 1.12.2 '@unrs/resolver-binding-linux-loong64-musl': 1.12.2 '@unrs/resolver-binding-linux-ppc64-gnu': 1.12.2 '@unrs/resolver-binding-linux-riscv64-gnu': 1.12.2 '@unrs/resolver-binding-linux-riscv64-musl': 1.12.2 '@unrs/resolver-binding-linux-s390x-gnu': 1.12.2 '@unrs/resolver-binding-linux-x64-gnu': 1.12.2 '@unrs/resolver-binding-linux-x64-musl': 1.12.2 '@unrs/resolver-binding-openharmony-arm64': 1.12.2 '@unrs/resolver-binding-wasm32-wasi': 1.12.2 '@unrs/resolver-binding-win32-arm64-msvc': 1.12.2 '@unrs/resolver-binding-win32-ia32-msvc': 1.12.2 '@unrs/resolver-binding-win32-x64-msvc': 1.12.2 update-browserslist-db@1.2.3(browserslist@4.28.5): dependencies: browserslist: 4.28.5 escalade: 3.2.0 picocolors: 1.1.1 uri-js@4.4.1: dependencies: punycode: 2.3.1 url-join@5.0.0: {} util-deprecate@1.0.2: {} validate-npm-package-license@3.0.4: dependencies: spdx-correct: 3.2.0 spdx-expression-parse: 3.0.1 vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0): dependencies: '@oxc-project/types': 0.138.0 '@oxlint/plugins': 1.68.0 '@vitest/browser': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10) '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10) '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 '@voidzero-dev/vite-plus-core': 0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0) oxfmt: 0.57.0(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)) oxlint: 1.72.0(oxlint-tsgolint@0.24.0)(vite-plus@0.2.4(@types/node@26.1.1)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(typescript@7.0.2)(yaml@2.9.0)) oxlint-tsgolint: 0.24.0 vitest: 4.1.10(@types/node@26.1.1)(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) optionalDependencies: '@voidzero-dev/vite-plus-darwin-arm64': 0.2.4 '@voidzero-dev/vite-plus-darwin-x64': 0.2.4 '@voidzero-dev/vite-plus-linux-arm64-gnu': 0.2.4 '@voidzero-dev/vite-plus-linux-arm64-musl': 0.2.4 '@voidzero-dev/vite-plus-linux-x64-gnu': 0.2.4 '@voidzero-dev/vite-plus-linux-x64-musl': 0.2.4 '@voidzero-dev/vite-plus-win32-arm64-msvc': 0.2.4 '@voidzero-dev/vite-plus-win32-x64-msvc': 0.2.4 transitivePeerDependencies: - '@arethetypeswrong/core' - '@edge-runtime/vm' - '@opentelemetry/api' - '@types/node' - '@vitejs/devtools' - '@vitest/coverage-istanbul' - '@vitest/coverage-v8' - '@vitest/ui' - bufferutil - esbuild - happy-dom - jiti - jsdom - less - msw - publint - sass - sass-embedded - stylus - sugarss - svelte - terser - tsx - typescript - unplugin-unused - unrun - utf-8-validate - vite - yaml vitest@4.1.10(@types/node@26.1.1)(@vitest/browser-preview@4.1.10)(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)): dependencies: '@vitest/expect': 4.1.10 '@vitest/mocker': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)) '@vitest/pretty-format': 4.1.10 '@vitest/runner': 4.1.10 '@vitest/snapshot': 4.1.10 '@vitest/spy': 4.1.10 '@vitest/utils': 4.1.10 es-module-lexer: 2.3.0 expect-type: 1.4.0 magic-string: 0.30.21 obug: 2.1.3 pathe: 2.0.3 picomatch: 4.0.5 std-env: 4.2.0 tinybench: 2.9.0 tinyexec: 1.2.4 tinyglobby: 0.2.17 tinyrainbow: 3.1.0 vite: '@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0)' why-is-node-running: 2.3.0 optionalDependencies: '@types/node': 26.1.1 '@vitest/browser-preview': 4.1.10(@voidzero-dev/vite-plus-core@0.2.4(@types/node@26.1.1)(typescript@7.0.2)(yaml@2.9.0))(vitest@4.1.10) transitivePeerDependencies: - msw web-worker@1.5.0: {} which-boxed-primitive@1.1.1: dependencies: is-bigint: 1.1.0 is-boolean-object: 1.2.2 is-number-object: 1.1.1 is-string: 1.1.1 is-symbol: 1.1.1 which-builtin-type@1.2.1: dependencies: call-bound: 1.0.4 function.prototype.name: 1.2.0 has-tostringtag: 1.0.2 is-async-function: 2.1.1 is-date-object: 1.1.0 is-finalizationregistry: 1.1.1 is-generator-function: 1.1.2 is-regex: 1.2.1 is-weakref: 1.1.1 isarray: 2.0.5 which-boxed-primitive: 1.1.1 which-collection: 1.0.2 which-typed-array: 1.1.22 which-collection@1.0.2: dependencies: is-map: 2.0.3 is-set: 2.0.3 is-weakmap: 2.0.2 is-weakset: 2.0.4 which-typed-array@1.1.22: dependencies: available-typed-arrays: 1.0.7 call-bind: 1.0.9 call-bound: 1.0.4 for-each: 0.3.5 get-proto: 1.0.1 gopd: 1.2.0 has-tostringtag: 1.0.2 which@2.0.2: dependencies: isexe: 2.0.0 which@7.0.0: dependencies: isexe: 4.0.0 why-is-node-running@2.3.0: dependencies: siginfo: 2.0.0 stackback: 0.0.2 word-wrap@1.2.5: {} wordwrap@1.0.0: {} wrap-ansi@10.0.0: dependencies: ansi-styles: 6.2.3 string-width: 8.2.2 strip-ansi: 7.2.0 wrap-ansi@7.0.0: dependencies: ansi-styles: 4.3.0 string-width: 4.2.3 strip-ansi: 6.0.1 wrap-ansi@9.0.2: dependencies: ansi-styles: 6.2.3 string-width: 7.2.0 strip-ansi: 7.2.0 ws@8.21.0: {} xtend@4.0.2: {} y18n@5.0.8: {} yallist@3.1.1: {} yaml@2.9.0: optional: true yargs-parser@20.2.9: {} yargs-parser@22.0.0: {} yargs@16.2.2: dependencies: cliui: 7.0.4 escalade: 3.2.0 get-caller-file: 2.0.5 require-directory: 2.1.1 string-width: 4.2.3 y18n: 5.0.8 yargs-parser: 20.2.9 yargs@18.0.0: dependencies: cliui: 9.0.1 escalade: 3.2.0 get-caller-file: 2.0.5 string-width: 7.2.0 y18n: 5.0.8 yargs-parser: 22.0.0 yocto-queue@0.1.0: {} yoctocolors@2.1.2: {} zod-validation-error@4.0.2(zod@4.4.3): dependencies: zod: 4.4.3 zod@4.4.3: {} ldapts-ldapts-91d5f4e/pnpm-workspace.yaml000066400000000000000000000003701522446626000205520ustar00rootroot00000000000000allowBuilds: esbuild: true unrs-resolver: true savePrefix: '' catalog: vite: npm:@voidzero-dev/vite-plus-core@0.2.4 vite-plus: 0.2.4 overrides: vite: 'catalog:' peerDependencyRules: allowAny: - vite allowedVersions: vite: '*' ldapts-ldapts-91d5f4e/release.config.mjs000066400000000000000000000023361522446626000203230ustar00rootroot00000000000000export default { branches: [ 'main', { name: 'beta', prerelease: 'beta', }, ], plugins: [ [ '@semantic-release/commit-analyzer', { preset: 'angular', releaseRules: [ { type: 'docs', release: 'patch' }, { type: 'feat', release: 'minor' }, { type: 'fix', release: 'patch' }, { type: 'test', release: 'patch' }, { type: 'chore', release: 'patch' }, { type: 'chore', scope: 'deps', release: false }, ], parserOpts: { // eslint-disable-next-line security/detect-unsafe-regex headerPattern: /^(\w+)(?:\(([^)]+)\))?: (.+)$/, headerCorrespondence: ['type', 'scope', 'subject'], }, }, ], '@semantic-release/release-notes-generator', [ '@semantic-release/changelog', { changelogFile: 'CHANGELOG.md', }, ], '@semantic-release/npm', [ '@semantic-release/github', { assets: [], }, ], [ '@semantic-release/git', { assets: ['CHANGELOG.md', 'package.json'], message: `chore: release \${nextRelease.version} [skip ci]\n\n\${nextRelease.notes}`, }, ], ], }; ldapts-ldapts-91d5f4e/src/000077500000000000000000000000001522446626000155075ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/Attribute.ts000066400000000000000000000043001522446626000200170ustar00rootroot00000000000000import { TextDecoder } from 'node:util'; import { type BerReader, type BerWriter } from './ber/index.js'; import { Ber } from './ber/index.js'; import { ProtocolOperation } from './ProtocolOperation.js'; const utfDecoder = new TextDecoder('utf8', { fatal: true }); export interface AttributeOptions { type?: string; values?: Buffer[] | string[]; } export class Attribute { private buffers: Buffer[] = []; public type: string; public values: Buffer[] | string[]; public constructor(options: AttributeOptions = {}) { this.type = options.type ?? ''; this.values = options.values ?? []; } public get parsedBuffers(): Buffer[] { return this.buffers; } public write(writer: BerWriter): void { writer.startSequence(); const { type } = this; writer.writeString(type); writer.startSequence(ProtocolOperation.LBER_SET); if (this.values.length) { for (const value of this.values) { if (Buffer.isBuffer(value)) { writer.writeBuffer(value, Ber.OctetString); } else { writer.writeString(value); } } } else { writer.writeStringArray([]); } writer.endSequence(); writer.endSequence(); } public parse(reader: BerReader): void { reader.readSequence(); this.type = reader.readString() ?? ''; const isBinaryType = this._isBinaryType(); if (reader.peek() === ProtocolOperation.LBER_SET) { if (reader.readSequence(ProtocolOperation.LBER_SET)) { const end = reader.offset + reader.length; while (reader.offset < end) { const buffer = reader.readString(Ber.OctetString, true) ?? Buffer.alloc(0); this.buffers.push(buffer); if (isBinaryType) { (this.values as Buffer[]).push(buffer); } else { try { const decoded = utfDecoder.decode(buffer); (this.values as string[]).push(decoded); } catch { // If the buffer cannot be decoded as UTF-8, treat it as binary data (this.values as Buffer[]).push(buffer); } } } } } } private _isBinaryType(): boolean { return /;binary$/i.test(this.type || ''); } } ldapts-ldapts-91d5f4e/src/Change.ts000066400000000000000000000032761522446626000172540ustar00rootroot00000000000000import { Attribute } from './Attribute.js'; import { type BerReader, type BerWriter } from './ber/index.js'; export interface ChangeOptions { operation?: 'add' | 'delete' | 'replace'; modification: Attribute; } export class Change { public operation: 'add' | 'delete' | 'replace'; public modification: Attribute; public constructor( options: ChangeOptions = { modification: new Attribute(), }, ) { this.operation = options.operation ?? 'add'; this.modification = options.modification; } public write(writer: BerWriter): void { writer.startSequence(); switch (this.operation) { case 'add': writer.writeEnumeration(0x00); break; case 'delete': writer.writeEnumeration(0x01); break; case 'replace': writer.writeEnumeration(0x02); break; default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions throw new Error(`Unknown change operation: ${this.operation}`); } this.modification.write(writer); writer.endSequence(); } public parse(reader: BerReader): void { reader.readSequence(); const operation = reader.readEnumeration(); switch (operation) { case 0x00: this.operation = 'add'; break; case 0x01: this.operation = 'delete'; break; case 0x02: this.operation = 'replace'; break; case null: throw new Error(`Unknown change operation - operation value`); default: throw new Error(`Unknown change operation: 0x${operation.toString(16)}`); } this.modification = new Attribute(); this.modification.parse(reader); } } ldapts-ldapts-91d5f4e/src/Client.ts000066400000000000000000001270731522446626000173070ustar00rootroot00000000000000import * as crypto from 'node:crypto'; import * as net from 'node:net'; import * as tls from 'node:tls'; import { debuglog } from 'node:util'; import { Attribute } from './Attribute.js'; import { type Change } from './Change.js'; import { type Control } from './controls/Control.js'; import { PagedResultsControl } from './controls/PagedResultsControl.js'; import { type DN } from './dn/DN.js'; import { type MessageParserError } from './errors/MessageParserError.js'; import { FilterParser } from './FilterParser.js'; import { type Filter } from './filters/Filter.js'; import { PresenceFilter } from './filters/PresenceFilter.js'; import { MessageParser } from './MessageParser.js'; import { MessageResponseStatus } from './MessageResponseStatus.js'; import { AbandonRequest, AddRequest, BindRequest, CompareRequest, CompareResult, DeleteRequest, ExtendedRequest, ModifyDNRequest, ModifyRequest, SASL_MECHANISMS, SearchEntry, SearchReference, SearchRequest, SearchResponse, UnbindRequest, } from './messages/index.js'; import { type AddResponse, type BindResponse, type CompareResponse, type DeleteResponse, type Entry, type ExtendedResponse, type ModifyDNResponse, type ModifyResponse, type SaslMechanism, } from './messages/index.js'; import { type Message } from './messages/Message.js'; import { type MessageResponse } from './messages/MessageResponse.js'; import { StatusCodeParser } from './StatusCodeParser.js'; const MAX_MESSAGE_ID = 2 ** 31 - 1; const logDebug = debuglog('ldapts'); type SocketWithId = { id?: string } & (net.Socket | tls.TLSSocket); export interface ClientOptions { /** * A valid LDAP URL (proto/host/port only) */ url: string; /** * Milliseconds client should let operations live for before timing out (Default: no timeout) */ timeout?: number; /** * Milliseconds client should wait before timing out on TCP connections */ connectTimeout?: number; /** * Additional options passed to TLS connection layer when connecting via ldaps:// */ tlsOptions?: tls.ConnectionOptions; /** * Force strict DN parsing for client methods (Default: true) */ strictDN?: boolean; /** * Custom function used to create the connection when connecting via ldap://. Called with the * parsed port and host from the url. Defaults to `net.connect`. Useful for supplying your own * transport: a proxied or tunneled connection, a Unix socket, or an existing socket * (e.g. `createConnection: () => myExistingSocket`). */ createConnection?: typeof net.connect; /** * Custom function used to create the connection when connecting via ldaps:// (called with the * parsed port, host, and `tlsOptions`), and to upgrade the connection during `startTLS()` * (called with the connection options for the existing socket). Defaults to `tls.connect`. */ createSecureConnection?: typeof tls.connect; /** * When true, the client remembers the last successful bind and automatically replays it after * the connection is transparently re-established (e.g. when the server closed an idle * connection). Without this, operations that trigger a reconnect run on an unauthenticated * connection. (Default: false) * * Note: the last bind credentials are retained in memory for the lifetime of the client (they * are cleared by `unbind()`), and rebinding is not applied to sessions upgraded with * `startTLS()` since the replayed bind would occur before the connection is upgraded again. */ autoRebind?: boolean; } interface MessageDetails { message: Message; searchEntries?: SearchEntry[]; searchReferences?: SearchReference[]; resolve: (message?: MessageResponse) => void; reject: (err: Error) => void; timeoutTimer: NodeJS.Timeout | null; socket: SocketWithId; } export interface SearchPageOptions { /** * Number of SearchEntries to return per page for a search request. If the page size is greater than or equal to the * sizeLimit value, the server should ignore the control as the request can be satisfied in a single page. */ pageSize?: number; } export interface SearchOptions { /** * Specifies how broad the search context is: * - base - Indicates that only the entry specified as the search base should be considered. None of its subordinates will be considered. * - one - Indicates that only the immediate children of the entry specified as the search base should be considered. The base entry itself should not be considered, nor any descendants of the immediate children of the base entry. * - sub - Indicates that the entry specified as the search base, and all of its subordinates to any depth, should be considered. * - children or subordinates - Indicates that the entry specified by the search base should not be considered, but all of its subordinates to any depth should be considered. */ scope?: 'base' | 'children' | 'one' | 'sub' | 'subordinates'; /** * Specifies how the server must treat references to other entries: * - never - Never dereferences entries, returns alias objects instead. The alias contains the reference to the real entry. * - always - Always returns the referenced entries, not the alias object. * - search - While searching subordinates of the base object, dereferences any alias within the search scope. Dereferenced objects become the bases of further search scopes where the Search operation is also applied by the server. The server should eliminate duplicate entries that arise due to alias dereferencing while searching. * - find - Dereferences aliases in locating the base object of the search, but not when searching subordinates of the base object. */ derefAliases?: 'always' | 'find' | 'never' | 'search'; /** * If true, attribute values should be included in the entries that are returned; otherwise entries that match the search criteria should be returned containing only the attribute descriptions for the attributes contained in that entry but should not include the values for those attributes. */ returnAttributeValues?: boolean; /** * This specifies the maximum number of entries that should be returned from the search. A value of zero indicates no limit. Note that the server may also impose a size limit for the search operation, and in that case the smaller of the client-requested and server-imposed size limits will be enforced. */ sizeLimit?: number; /** * This specifies the maximum length of time, in seconds, that the server should spend processing the search. A value of zero indicates no limit. Note that the server may also impose a time limit for the search operation, and in that case the smaller of the client-requested and server-imposed time limits will be enforced. */ timeLimit?: number; /** * Used to allow paging and specify the page size */ paged?: SearchPageOptions | boolean; /** * The filter of the search request. It must conform to the LDAP filter syntax specified in RFC4515 */ filter?: Filter | string; /** * A set of attributes to request for inclusion in entries that match the search criteria and are returned to the client. If a specific set of attribute descriptions are listed, then only those attributes should be included in matching entries. The special value “*” indicates that all user attributes should be included in matching entries. The special value “+” indicates that all operational attributes should be included in matching entries. The special value “1.1” indicates that no attributes should be included in matching entries. Some servers may also support the ability to use the “@” symbol followed by an object class name (e.g., “@inetOrgPerson”) to request all attributes associated with that object class. If the set of attributes to request is empty, then the server should behave as if the value “*” was specified to request that all user attributes be included in entries that are returned. */ attributes?: string[]; /** * List of attributes to explicitly return as buffers */ explicitBufferAttributes?: string[]; } export interface SearchResult { searchEntries: Entry[]; searchReferences: string[]; } export class Client { private clientOptions: ClientOptions; private messageId = 1; private readonly host: string; private readonly port: number; private readonly secure: boolean; private connected = false; private bound = false; private startTLSUpgraded = false; private rebind?: () => Promise; private socket?: SocketWithId; private connectTimer?: NodeJS.Timeout; private readonly messageParser = new MessageParser(); private readonly messageDetailsByMessageId = new Map(); public constructor(options: ClientOptions) { this.clientOptions = options; this.clientOptions.timeout ??= 0; this.clientOptions.connectTimeout ??= 0; this.clientOptions.strictDN = this.clientOptions.strictDN !== false; let parsedUrl: URL; try { parsedUrl = new URL(options.url); } catch { throw new Error(`${options.url} is an invalid LDAP URL (protocol)`); } // protocol includes trailing colon (e.g., 'ldap:') const scheme = parsedUrl.protocol.replace(/:$/, ''); if (scheme !== 'ldap' && scheme !== 'ldaps') { throw new Error(`${options.url} is an invalid LDAP URL (protocol)`); } const isSecureProtocol = scheme === 'ldaps'; // Check if tlsOptions has at least one defined property (not just an empty object or object with all undefined values) const hasTlsOptions = !!this.clientOptions.tlsOptions && Object.values(this.clientOptions.tlsOptions).some((value) => value !== undefined); this.secure = isSecureProtocol || hasTlsOptions; // hostname excludes port; for IPv6, it includes brackets (e.g., '[::1]') let host = parsedUrl.hostname; // Remove IPv6 brackets if present if (host.startsWith('[') && host.endsWith(']')) { host = host.slice(1, -1); } this.host = host || 'localhost'; // Default to 'localhost' if host is empty if (parsedUrl.port) { this.port = Number(parsedUrl.port); } else if (isSecureProtocol) { this.port = 636; } else { this.port = 389; } this.messageParser.on('error', (err: MessageParserError) => { if (err.messageDetails?.messageId) { const messageDetails = this.messageDetailsByMessageId.get(err.messageDetails.messageId.toString()); if (messageDetails) { this.messageDetailsByMessageId.delete(err.messageDetails.messageId.toString()); messageDetails.reject(err); return; } } if (err.stack) { logDebug(err.stack); } }); this.messageParser.on('message', this._handleSendResponse.bind(this)); } public get isConnected(): boolean { return !!this.socket && this.connected; } /** * True when the current connection has successfully bound. Becomes false when the connection * is closed or re-established, which makes it possible to detect a connection that was * transparently reconnected but is no longer authenticated. * @returns {boolean} True if the current connection is authenticated */ public get isBound(): boolean { return this.isConnected && this.bound; } public async startTLS(options: tls.ConnectionOptions = {}, controls?: Control | Control[]): Promise { if (!this.isConnected) { await this._connect(); } await this.exop('1.3.6.1.4.1.1466.20037', undefined, controls); const originalSocket = this.socket; if (originalSocket) { originalSocket.removeListener('data', this.socketDataHandler); // Reuse existing socket options.socket = originalSocket; } this.socket = await new Promise((resolve: (value: SocketWithId) => void, reject: (reason: Error) => void) => { const createSecureConnection = this.clientOptions.createSecureConnection ?? tls.connect; const secureSocket = createSecureConnection(options); secureSocket.once('secureConnect', () => { secureSocket.removeAllListeners('error'); secureSocket.on('data', this.socketDataHandler); secureSocket.on('error', () => { if (originalSocket) { originalSocket.destroy(); } }); resolve(secureSocket); }); secureSocket.once('error', (err: Error) => { secureSocket.removeAllListeners(); reject(err); }); }); if (originalSocket) { // Allows pending messages and unbind responses to be handled and cleaned up this.socket.id = originalSocket.id; } this.startTLSUpgraded = true; } /** * Performs a simple or sasl authentication against the server. * @param {string|DN|SaslMechanism} dnOrSaslMechanism * @param {string} [password] * @param {Control|Control[]} [controls] */ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents public async bind(dnOrSaslMechanism: DN | SaslMechanism | string, password?: string, controls?: Control | Control[]): Promise { if (controls && !Array.isArray(controls)) { controls = [controls]; } if (typeof dnOrSaslMechanism === 'string' && SASL_MECHANISMS.includes(dnOrSaslMechanism as SaslMechanism)) { await this.bindSASL(dnOrSaslMechanism, password, controls); return; } const req = new BindRequest({ messageId: this._nextMessageId(), dn: typeof dnOrSaslMechanism === 'string' ? dnOrSaslMechanism : dnOrSaslMechanism.toString(), password, controls, }); await this._sendBind(req); this._onBindSuccess(() => this.bind(dnOrSaslMechanism, password, controls)); } /** * Performs a sasl authentication against the server. * @param {string|SaslMechanism} mechanism * @param {string} [password] * @param {Control|Control[]} [controls] */ // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents public async bindSASL(mechanism: SaslMechanism | string, password?: string, controls?: Control | Control[]): Promise { if (controls && !Array.isArray(controls)) { controls = [controls]; } const req = new BindRequest({ messageId: this._nextMessageId(), mechanism, password, controls, }); await this._sendBind(req); this._onBindSuccess(() => this.bindSASL(mechanism, password, controls)); } /** * Used to create a new entry in the directory * @param {string|DN} dn - The DN of the entry to add * @param {Attribute[]|object} attributes - Array of attributes or object where keys are the name of each attribute * @param {Control|Control[]} [controls] */ public async add(dn: DN | string, attributes: Attribute[] | Record, controls?: Control | Control[]): Promise { await this._ensureConnected(); if (controls && !Array.isArray(controls)) { controls = [controls]; } let attributesToAdd; if (Array.isArray(attributes)) { attributesToAdd = attributes; } else { attributesToAdd = []; for (const [key, value] of Object.entries(attributes)) { let values: string[]; if (Array.isArray(value)) { values = value; // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition } else if (value == null) { values = [] as string[]; } else { values = [value]; } attributesToAdd.push( new Attribute({ type: key, values, }), ); } } const req = new AddRequest({ messageId: this._nextMessageId(), dn: typeof dn === 'string' ? dn : dn.toString(), attributes: attributesToAdd, controls, }); const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } } /** * Compares an attribute/value pair with an entry on the LDAP server. * @param {string|DN} dn - The DN of the entry to compare attributes with * @param {string} attribute * @param {string} value * @param {Control|Control[]} [controls] * @returns true if attribute and value match; otherwise false */ public async compare(dn: DN | string, attribute: string, value: string, controls?: Control | Control[]): Promise { await this._ensureConnected(); if (controls && !Array.isArray(controls)) { controls = [controls]; } const req = new CompareRequest({ messageId: this._nextMessageId(), dn: typeof dn === 'string' ? dn : dn.toString(), attribute, value, controls, }); const response = await this._send(req); switch (response?.status) { case CompareResult.compareTrue: return true; case CompareResult.compareFalse: return false; default: throw StatusCodeParser.parse(response); } } /** * Deletes an entry from the LDAP server. * @param {string|DN} dn - The DN of the entry to delete * @param {Control|Control[]} [controls] */ public async del(dn: DN | string, controls?: Control | Control[]): Promise { await this._ensureConnected(); if (controls && !Array.isArray(controls)) { controls = [controls]; } const req = new DeleteRequest({ messageId: this._nextMessageId(), dn: typeof dn === 'string' ? dn : dn.toString(), controls, }); const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } } /** * Performs an extended operation on the LDAP server. * @param {string} oid - The object identifier (OID) of the extended operation to perform * @param {string|Buffer} [value] * @param {Control|Control[]} [controls] * @returns exop result */ public async exop(oid: string, value?: Buffer | string, controls?: Control | Control[]): Promise<{ oid?: string; value?: string }> { await this._ensureConnected(); if (controls && !Array.isArray(controls)) { controls = [controls]; } const req = new ExtendedRequest({ messageId: this._nextMessageId(), oid, value, controls, }); const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } return { oid: result.oid, value: result.value, }; } /** * Performs an LDAP modify against the server. * @param {string|DN} dn - The DN of the entry to modify * @param {Change|Change[]} changes * @param {Control|Control[]} [controls] */ public async modify(dn: DN | string, changes: Change | Change[], controls?: Control | Control[]): Promise { await this._ensureConnected(); if (!Array.isArray(changes)) { changes = [changes]; } if (controls && !Array.isArray(controls)) { controls = [controls]; } const req = new ModifyRequest({ messageId: this._nextMessageId(), dn: typeof dn === 'string' ? dn : dn.toString(), changes, controls, }); const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } } /** * Performs an LDAP modifyDN against the server. * @param {string|DN} dn - The DN of the entry to modify * @param {string|DN} newDN - The new DN to move this entry to * @param {Control|Control[]} [controls] */ public async modifyDN(dn: DN | string, newDN: DN | string, controls?: Control | Control[]): Promise { await this._ensureConnected(); if (controls && !Array.isArray(controls)) { controls = [controls]; } let newSuperior: string | undefined; if (typeof newDN === 'string' && /[^\\],/.test(newDN)) { const parseIndex = newDN.search(/[^\\],/); newSuperior = newDN.slice(parseIndex + 2); newDN = newDN.slice(0, parseIndex + 1); } const req = new ModifyDNRequest({ messageId: this._nextMessageId(), dn: typeof dn === 'string' ? dn : dn.toString(), deleteOldRdn: true, newRdn: typeof newDN === 'string' ? newDN : newDN.toString(), newSuperior, controls, }); const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } } /** * Performs an LDAP search against the server. * @param {string|DN} baseDN - This specifies the base of the subtree in which the search is to be constrained. * @param {SearchOptions} [options] * @param {string|Filter} [options.filter] - The filter of the search request. It must conform to the LDAP filter syntax specified in RFC4515. Defaults to (objectclass=*) * @param {string} [options.scope] - Specifies how broad the search context is: * - base - Indicates that only the entry specified as the search base should be considered. None of its subordinates will be considered. * - one - Indicates that only the immediate children of the entry specified as the search base should be considered. The base entry itself should not be considered, nor any descendants of the immediate children of the base entry. * - sub - Indicates that the entry specified as the search base, and all of its subordinates to any depth, should be considered. * - children or subordinates - Indicates that the entry specified by the search base should not be considered, but all of its subordinates to any depth should be considered. * @param {string} [options.derefAliases] - Specifies how the server must treat references to other entries: * - never - Never dereferences entries, returns alias objects instead. The alias contains the reference to the real entry. * - always - Always returns the referenced entries, not the alias object. * - search - While searching subordinates of the base object, dereferences any alias within the search scope. Dereferenced objects become the bases of further search scopes where the Search operation is also applied by the server. The server should eliminate duplicate entries that arise due to alias dereferencing while searching. * - find - Dereferences aliases in locating the base object of the search, but not when searching subordinates of the base object. * @param {boolean} [options.returnAttributeValues] - If true, attribute values should be included in the entries that are returned; otherwise entries that match the search criteria should be returned containing only the attribute descriptions for the attributes contained in that entry but should not include the values for those attributes. * @param {number} [options.sizeLimit] - This specifies the maximum number of entries that should be returned from the search. A value of zero indicates no limit. Note that the server may also impose a size limit for the search operation, and in that case the smaller of the client-requested and server-imposed size limits will be enforced. * @param {number} [options.timeLimit] - This specifies the maximum length of time, in seconds, that the server should spend processing the search. A value of zero indicates no limit. Note that the server may also impose a time limit for the search operation, and in that case the smaller of the client-requested and server-imposed time limits will be enforced. * @param {boolean|SearchPageOptions} [options.paged] - Used to allow paging and specify the page size * @param {string[]} [options.attributes] - A set of attributes to request for inclusion in entries that match the search criteria and are returned to the client. If a specific set of attribute descriptions are listed, then only those attributes should be included in matching entries. The special value “*” indicates that all user attributes should be included in matching entries. The special value “+” indicates that all operational attributes should be included in matching entries. The special value “1.1” indicates that no attributes should be included in matching entries. Some servers may also support the ability to use the “@” symbol followed by an object class name (e.g., “@inetOrgPerson”) to request all attributes associated with that object class. If the set of attributes to request is empty, then the server should behave as if the value “*” was specified to request that all user attributes be included in entries that are returned. * @param {string[]} [options.explicitBufferAttributes] - List of attributes to explicitly return as buffers * @param {Control|Control[]} [controls] * @returns {Promise} */ public async search(baseDN: DN | string, options: SearchOptions = {}, controls?: Control | Control[]): Promise { await this._ensureConnected(); if (controls) { if (Array.isArray(controls)) { controls = controls.slice(0); } else { controls = [controls]; } // Make sure PagedResultsControl is not specified since it's handled internally for (const control of controls) { if (control instanceof PagedResultsControl) { throw new Error('Should not specify PagedResultsControl'); } } } else { controls = []; } let pageSize = 100; if (typeof options.paged === 'object' && options.paged.pageSize) { pageSize = options.paged.pageSize; } else if (options.sizeLimit && options.sizeLimit > 1) { // According to the RFC, servers should ignore the paging control if // pageSize >= sizelimit. Some might still send results, but it's safer // to stay under that figure when assigning a default value. pageSize = options.sizeLimit - 1; } let pagedResultsControl: PagedResultsControl | undefined; const shouldPage = !!options.paged; if (shouldPage) { pagedResultsControl = new PagedResultsControl({ value: { size: pageSize, }, }); controls.push(pagedResultsControl); } let filter: Filter; if (options.filter) { if (typeof options.filter === 'string') { filter = FilterParser.parseString(options.filter); } else { filter = options.filter; } } else { filter = new PresenceFilter({ attribute: 'objectclass' }); } const searchRequest = new SearchRequest({ messageId: -1, // NOTE: This will be set from _sendRequest() baseDN: typeof baseDN === 'string' ? baseDN : baseDN.toString(), scope: options.scope, filter, attributes: options.attributes, explicitBufferAttributes: options.explicitBufferAttributes, returnAttributeValues: options.returnAttributeValues, sizeLimit: options.sizeLimit, timeLimit: options.timeLimit, controls, }); const searchResult: SearchResult = { searchEntries: [], searchReferences: [], }; await this._sendSearch(searchRequest, searchResult, shouldPage, pageSize, pagedResultsControl); return searchResult; } public async *searchPaginated(baseDN: DN | string, options: SearchOptions = {}, controls?: Control | Control[]): AsyncGenerator { await this._ensureConnected(); if (controls) { if (Array.isArray(controls)) { controls = controls.slice(0); } else { controls = [controls]; } // Make sure PagedResultsControl is not specified since it's handled internally for (const control of controls) { if (control instanceof PagedResultsControl) { throw new Error('Should not specify PagedResultsControl'); } } } else { controls = []; } let pageSize = 100; if (typeof options.paged === 'object' && options.paged.pageSize) { pageSize = options.paged.pageSize; } else if (options.sizeLimit && options.sizeLimit > 1) { // According to the RFC, servers should ignore the paging control if // pageSize >= sizelimit. Some might still send results, but it's safer // to stay under that figure when assigning a default value. pageSize = options.sizeLimit - 1; } let pagedResultsControl: PagedResultsControl | undefined; pagedResultsControl = new PagedResultsControl({ value: { size: pageSize, }, }); controls.push(pagedResultsControl); let filter: Filter; if (options.filter) { if (typeof options.filter === 'string') { filter = FilterParser.parseString(options.filter); } else { filter = options.filter; } } else { filter = new PresenceFilter({ attribute: 'objectclass' }); } const searchRequest = new SearchRequest({ messageId: -1, // NOTE: This will be set from _sendRequest() baseDN: typeof baseDN === 'string' ? baseDN : baseDN.toString(), scope: options.scope, filter, attributes: options.attributes, explicitBufferAttributes: options.explicitBufferAttributes, returnAttributeValues: options.returnAttributeValues, sizeLimit: options.sizeLimit, timeLimit: options.timeLimit, controls, }); do { const searchResult: SearchResult = { searchEntries: [], searchReferences: [], }; // eslint-disable-next-line no-await-in-loop pagedResultsControl = await this._sendSearch(searchRequest, searchResult, true, pageSize, pagedResultsControl, false); yield searchResult; } while (pagedResultsControl); } /** * Unbinds this client from the LDAP server. * @returns {void|Promise} void if not connected; otherwise returns a promise to the request to disconnect */ public async unbind(): Promise { try { this.rebind = undefined; if (!this.connected || !this.socket) { return; } const req = new UnbindRequest({ messageId: this._nextMessageId(), }); await this._send(req); } finally { this._destroySocket(this.socket); } } public [Symbol.asyncDispose](): Promise { return this.unbind(); } private async _sendBind(req: BindRequest): Promise { if (!this.isConnected) { await this._connect(); } const result = await this._send(req); if (result?.status !== MessageResponseStatus.Success) { throw StatusCodeParser.parse(result); } } private async _sendSearch( searchRequest: SearchRequest, searchResult: SearchResult, paged: boolean, pageSize: number, pagedResultsControl?: PagedResultsControl, fetchAll = true, ): Promise { searchRequest.messageId = this._nextMessageId(); const result = await this._send(searchRequest); if (result?.status !== MessageResponseStatus.Success && !(result?.status === MessageResponseStatus.SizeLimitExceeded && searchRequest.sizeLimit)) { throw StatusCodeParser.parse(result); } for (const searchEntry of result.searchEntries) { searchResult.searchEntries.push(searchEntry.toObject(searchRequest.attributes, searchRequest.explicitBufferAttributes)); } for (const searchReference of result.searchReferences) { searchResult.searchReferences.push(...searchReference.uris); } // Recursively search if paging is specified if (paged && (result.searchEntries.length || result.searchReferences.length) && pagedResultsControl) { let pagedResultsFromResponse: PagedResultsControl | undefined; for (const control of result.controls ?? []) { if (control instanceof PagedResultsControl) { pagedResultsFromResponse = control; break; } } if (pagedResultsFromResponse?.value?.cookie?.length) { // Recursively keep searching pagedResultsControl.value = pagedResultsControl.value ?? { size: pageSize, }; pagedResultsControl.value.cookie = pagedResultsFromResponse.value.cookie; if (fetchAll) { await this._sendSearch(searchRequest, searchResult, paged, pageSize, pagedResultsControl, true); } else { return pagedResultsControl; } } } return undefined; } private readonly socketDataHandler = (data: Buffer): void => { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this.messageParser) { this.messageParser.read(data, this.messageDetailsByMessageId); } }; private _nextMessageId(): number { this.messageId += 1; if (this.messageId >= MAX_MESSAGE_ID) { this.messageId = 1; } return this.messageId; } /** * Records a successful bind and, when autoRebind is enabled, remembers how to replay it after * a transparent reconnect. Binds on a startTLS-upgraded session are not replayed: the rebind * happens before the fresh connection could be upgraded again, so replaying would downgrade * the security of the credentials. * @param {Function} rebind - Replays the bind that just succeeded * @private */ private _onBindSuccess(rebind: () => Promise): void { this.bound = true; if (this.clientOptions.autoRebind && !this.startTLSUpgraded) { this.rebind = rebind; } } /** * Ensures the socket connection is established and, when autoRebind is enabled, replays the * last successful bind on a freshly re-established connection. * @returns {Promise} * @private */ private async _ensureConnected(): Promise { if (this.isConnected) { return; } await this._connect(); if (this.rebind && !this.bound) { await this.rebind(); } } /** * Open the socket connection * @returns {Promise|void} * @private */ private _connect(): Promise | void { if (this.isConnected) { return; } return new Promise((resolve, reject) => { if (this.secure) { const createSecureConnection = this.clientOptions.createSecureConnection ?? tls.connect; this.socket = createSecureConnection(this.port, this.host, this.clientOptions.tlsOptions); this.socket.id = crypto.randomUUID(); this.socket.once('secureConnect', () => { this._onConnect(resolve); }); } else { const createConnection = this.clientOptions.createConnection ?? net.connect; this.socket = createConnection(this.port, this.host); this.socket.id = crypto.randomUUID(); this.socket.once('connect', () => { this._onConnect(resolve); }); } this.socket.once('error', (err: Error) => { this._destroySocket(this.socket); reject(err); }); // A custom connection factory can return a socket that is already established, which will // not emit another 'connect'/'secureConnect' event. if (!this.socket.connecting && this.socket.readyState === 'open') { this._onConnect(resolve); return; } if (this.clientOptions.connectTimeout) { this.connectTimer = setTimeout(() => { this._destroySocket(this.socket); reject(new Error('Connection timeout')); }, this.clientOptions.connectTimeout); } }); } private _onConnect(next: () => void): void { if (this.connectTimer) { clearTimeout(this.connectTimer); this.connectTimer = undefined; } // Clear out event listeners from _connect() if (this.socket) { this.socket.removeAllListeners('error'); this.socket.removeAllListeners('connect'); this.socket.removeAllListeners('secureConnect'); } this.connected = true; // region Socket events handlers const socketError = (err: Error): void => { // Clean up any pending messages for (const [key, messageDetails] of this.messageDetailsByMessageId.entries()) { if (messageDetails.message instanceof UnbindRequest) { // Consider unbind as success since the connection is closed. messageDetails.resolve(); } else { messageDetails.reject( new Error( `Socket error. Message type: ${messageDetails.message.constructor.name} (0x${messageDetails.message.protocolOperation.toString(16)})\n${ err.message || (err.stack ?? 'Unknown socket error') }`, ), ); } this.messageDetailsByMessageId.delete(key); } if (this.socket) { this.socket.destroy(); } }; function socketEnd(this: SocketWithId): void { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this) { // Acknowledge to other end of the connection that the connection is ended. this.end(); } } function socketTimeout(this: SocketWithId): void { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this) { // Acknowledge to other end of the connection that the connection is ended. this.end(); } } // eslint-disable-next-line @typescript-eslint/no-this-alias const clientInstance = this; function socketClose(this: SocketWithId): void { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (this) { this.removeListener('error', socketError); this.removeListener('close', socketClose); this.removeListener('data', clientInstance.socketDataHandler); this.removeListener('end', socketEnd); this.removeListener('timeout', socketTimeout); } if (this === clientInstance.socket) { clientInstance.connected = false; clientInstance.bound = false; clientInstance.startTLSUpgraded = false; delete clientInstance.socket; if (clientInstance.connectTimer) { clearTimeout(clientInstance.connectTimer); // Timeout get destroyed but also clear up reference clientInstance.connectTimer = undefined; } } // Clean up any pending messages for (const [key, messageDetails] of clientInstance.messageDetailsByMessageId.entries()) { if (messageDetails.socket.id === this.id) { if (messageDetails.message instanceof UnbindRequest) { // Consider unbind as success since the connection is closed. messageDetails.resolve(); } else { messageDetails.reject( new Error( `Connection closed before message response was received. Message type: ${messageDetails.message.constructor.name} (0x${messageDetails.message.protocolOperation.toString(16)})`, ), ); } clientInstance.messageDetailsByMessageId.delete(key); } } } // endregion // Hook up event listeners if (this.socket) { this.socket.on('error', socketError); this.socket.on('close', socketClose); this.socket.on('data', this.socketDataHandler); this.socket.on('end', socketEnd); this.socket.on('timeout', socketTimeout); } next(); } private _destroySocket(socket?: SocketWithId): void { if (socket) { if (this.connectTimer) { clearTimeout(this.connectTimer); this.connectTimer = undefined; } // Ignore any error since the connection is being closed socket.removeAllListeners('error'); socket.on('error', () => { // Ignore NOOP }); // send FIN packet first for the sake of ldap servers socket.end(); socket.destroy(); if (socket === this.socket) { this.connected = false; this.bound = false; this.startTLSUpgraded = false; this.socket = undefined; } } } /** * Sends request message to the ldap server over the connected socket. Each message request is given a * unique id (messageId), used to identify the associated response when it is sent back over the socket. * @returns {Promise} * @private * @param {object} message */ private _send(message: Message): Promise { if (!this.connected || !this.socket) { throw new Error('Socket connection not established'); } const messageContentBuffer = message.write(); // eslint-disable-next-line func-style let messageResolve: (messageResponse?: TMessageResponse) => void = () => { // Ignore this as a NOOP }; // eslint-disable-next-line func-style let messageReject: (err: Error) => void = () => { // Ignore this as a NOOP }; const messageTimeoutId = this.clientOptions.timeout ? setTimeout(() => { const messageDetails = this.messageDetailsByMessageId.get(message.messageId.toString()); if (messageDetails) { this._destroySocket(messageDetails.socket); messageReject(new Error(`${message.constructor.name}: Operation timed out`)); } }, this.clientOptions.timeout) : null; const sendPromise = new Promise((resolve, reject) => { messageResolve = resolve; messageReject = reject; }).finally(() => { if (messageTimeoutId) { clearTimeout(messageTimeoutId); } }); this.messageDetailsByMessageId.set(message.messageId.toString(), { message, // @ts-expect-error - Both parameter types extend MessageResponse but typescript sees them as different types resolve: messageResolve, reject: messageReject, timeoutTimer: messageTimeoutId, socket: this.socket, }); if ((message as BindRequest).password) { logDebug( `Sending message: ${JSON.stringify({ // eslint-disable-next-line @typescript-eslint/no-misused-spread ...message, password: '__redacted__', })}`, ); } else { logDebug(`Sending message: ${JSON.stringify(message)}`); } // Send the message to the socket this.socket.write(messageContentBuffer, () => { if (message instanceof AbandonRequest) { logDebug(`Abandoned message: ${message.messageId}`); this.messageDetailsByMessageId.delete(message.messageId.toString()); messageResolve(); } else if (message instanceof UnbindRequest) { logDebug('Unbind success. Ending socket'); if (this.socket) { this._destroySocket(this.socket); } } else { // NOTE: messageResolve will be called as 'data' events come from the socket logDebug('Message sent successfully.'); } }); return sendPromise; } private _handleSendResponse(message: Message): void { const messageDetails = this.messageDetailsByMessageId.get(message.messageId.toString()); if (messageDetails) { // When performing a search, an arbitrary number of SearchEntry and SearchReference messages come through with the // same messageId as the SearchRequest. Finally, a SearchResponse will come through to complete the request. if (message instanceof SearchEntry) { messageDetails.searchEntries = messageDetails.searchEntries ?? []; messageDetails.searchEntries.push(message); } else if (message instanceof SearchReference) { messageDetails.searchReferences = messageDetails.searchReferences ?? []; messageDetails.searchReferences.push(message); } else if (message instanceof SearchResponse) { // Assign any previously collected entries & references if (messageDetails.searchEntries) { message.searchEntries.push(...messageDetails.searchEntries); } if (messageDetails.searchReferences) { message.searchReferences.push(...messageDetails.searchReferences); } this.messageDetailsByMessageId.delete(message.messageId.toString()); messageDetails.resolve(message); } else { this.messageDetailsByMessageId.delete(message.messageId.toString()); messageDetails.resolve(message as MessageResponse); } } else { logDebug(`Unable to find details related to message response: ${JSON.stringify(message)}`); } } } ldapts-ldapts-91d5f4e/src/ControlParser.ts000066400000000000000000000035351522446626000206620ustar00rootroot00000000000000import { Ber, BerReader } from './ber/index.js'; import { type Control } from './controls/Control.js'; import { EntryChangeNotificationControl, PagedResultsControl, PersistentSearchControl, ServerSideSortingRequestControl } from './controls/index.js'; // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class ControlParser { public static parse(reader: BerReader, requestControls: Control[]): Control | null { if (reader.readSequence() === null) { return null; } let type = ''; let critical = false; let value: Buffer = Buffer.alloc(0); if (reader.length) { const end = reader.offset + reader.length; type = reader.readString() ?? ''; if (reader.offset < end) { if (reader.peek() === Ber.Boolean) { critical = reader.readBoolean() ?? false; } } if (reader.offset < end) { value = reader.readString(Ber.OctetString, true) ?? Buffer.alloc(0); } } let control: Control | undefined; switch (type) { case EntryChangeNotificationControl.type: control = new EntryChangeNotificationControl({ critical, }); break; case PagedResultsControl.type: control = new PagedResultsControl({ critical, }); break; case PersistentSearchControl.type: control = new PersistentSearchControl({ critical, }); break; case ServerSideSortingRequestControl.type: control = new ServerSideSortingRequestControl({ critical, }); break; default: control = requestControls.find((requestControl) => requestControl.type === type); break; } if (!control) { return null; } const controlReader = new BerReader(value); control.parse(controlReader); return control; } } ldapts-ldapts-91d5f4e/src/FilterParser.ts000066400000000000000000000263131522446626000204660ustar00rootroot00000000000000import { type BerReader } from './ber/index.js'; import { type ExtensibleFilterOptions } from './filters/ExtensibleFilter.js'; import { type Filter } from './filters/Filter.js'; import { AndFilter, ApproximateFilter, EqualityFilter, ExtensibleFilter, GreaterThanEqualsFilter, LessThanEqualsFilter, NotFilter, OrFilter, PresenceFilter, SubstringFilter, } from './filters/index.js'; import { SearchFilter } from './SearchFilter.js'; interface ParseStringResult { end: number; filter: Filter; } interface Substring { initial: string; any: string[]; final: string; } // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class FilterParser { public static parseString(filterString: string): Filter { if (!filterString) { throw new Error('Filter cannot be empty'); } // Wrap input in parens if it wasn't already if (!filterString.startsWith('(')) { filterString = `(${filterString})`; } const parseResult = FilterParser._parseString(filterString, 0, filterString); const end = filterString.length - 1; if (parseResult.end < end) { throw new Error(`Unbalanced parens in filter string: ${filterString}`); } return parseResult.filter; } /* * A filter looks like this coming in: * Filter ::= CHOICE { * and [0] SET OF Filter, * or [1] SET OF Filter, * not [2] Filter, * equalityMatch [3] AttributeValueAssertion, * substrings [4] SubstringFilter, * greaterOrEqual [5] AttributeValueAssertion, * lessOrEqual [6] AttributeValueAssertion, * present [7] AttributeType, * approxMatch [8] AttributeValueAssertion, * extensibleMatch [9] MatchingRuleAssertion --v3 only * } * * SubstringFilter ::= SEQUENCE { * type AttributeType, * SEQUENCE OF CHOICE { * initial [0] IA5String, * any [1] IA5String, * final [2] IA5String * } * } * * The extensibleMatch was added in LDAPv3: * * MatchingRuleAssertion ::= SEQUENCE { * matchingRule [1] MatchingRuleID OPTIONAL, * type [2] AttributeDescription OPTIONAL, * matchValue [3] AssertionValue, * dnAttributes [4] BOOLEAN DEFAULT FALSE * } */ public static parse(reader: BerReader): Filter { const type: number | null = reader.readSequence(); let filter: Filter; switch (type) { case SearchFilter.and: { const andFilters = FilterParser._parseSet(reader); filter = new AndFilter({ filters: andFilters, }); break; } case SearchFilter.approxMatch: filter = new ApproximateFilter(); filter.parse(reader); break; case SearchFilter.equalityMatch: filter = new EqualityFilter(); filter.parse(reader); break; case SearchFilter.extensibleMatch: filter = new ExtensibleFilter(); filter.parse(reader); break; case SearchFilter.greaterOrEqual: filter = new GreaterThanEqualsFilter(); filter.parse(reader); break; case SearchFilter.lessOrEqual: filter = new LessThanEqualsFilter(); filter.parse(reader); break; case SearchFilter.not: { const innerFilter = FilterParser.parse(reader); filter = new NotFilter({ filter: innerFilter, }); break; } case SearchFilter.or: { const orFilters = FilterParser._parseSet(reader); filter = new OrFilter({ filters: orFilters, }); break; } case SearchFilter.present: filter = new PresenceFilter(); filter.parse(reader); break; case SearchFilter.substrings: filter = new SubstringFilter(); filter.parse(reader); break; default: throw new Error(`Invalid search filter type: 0x${type ?? ''}`); } return filter; } private static _parseString(filterString: string, start: number, fullString: string): ParseStringResult { let cursor = start; const { length } = filterString; let filter: Filter; if (filterString[cursor] !== '(') { throw new Error(`Missing paren: ${filterString}. Full string: ${fullString}`); } cursor += 1; switch (filterString[cursor]) { case '&': { cursor += 1; const children: Filter[] = []; do { const childResult = FilterParser._parseString(filterString, cursor, fullString); children.push(childResult.filter); cursor = childResult.end + 1; } while (cursor < length && filterString[cursor] !== ')'); filter = new AndFilter({ filters: children, }); break; } case '|': { cursor += 1; const children: Filter[] = []; do { const childResult = FilterParser._parseString(filterString, cursor, fullString); children.push(childResult.filter); cursor = childResult.end + 1; } while (cursor < length && filterString[cursor] !== ')'); filter = new OrFilter({ filters: children, }); break; } case '!': { const childResult = FilterParser._parseString(filterString, cursor + 1, fullString); filter = new NotFilter({ filter: childResult.filter, }); cursor = childResult.end + 1; break; } default: { const end = filterString.indexOf(')', cursor); if (end === -1) { throw new Error(`Unbalanced parens: ${filterString}. Full string: ${fullString}`); } filter = FilterParser._parseExpressionFilterFromString(filterString.substring(cursor, end)); cursor = end; } } return { end: cursor, filter, }; } private static _parseExpressionFilterFromString(filterString: string): Filter { let attribute: string; let remainingExpression: string; if (filterString.startsWith(':')) { // An extensible filter can have no attribute name (Only valid when using dn and * matching-rule evaluation) attribute = ''; remainingExpression = filterString; } else { const matches = /^[\w-]+/.exec(filterString); if (matches?.length) { [attribute] = matches; remainingExpression = filterString.slice(attribute.length); } else { throw new Error(`Invalid attribute name: ${filterString}`); } } if (remainingExpression === '=*') { return new PresenceFilter({ attribute, }); } if (remainingExpression.startsWith('=')) { remainingExpression = remainingExpression.slice(1); if (remainingExpression.includes('*')) { const escapedExpression = FilterParser._unescapeSubstring(remainingExpression); return new SubstringFilter({ attribute, initial: escapedExpression.initial, any: escapedExpression.any, final: escapedExpression.final, }); } return new EqualityFilter({ attribute, value: FilterParser._unescapeHexValues(remainingExpression), }); } if (remainingExpression.startsWith('>') && remainingExpression[1] === '=') { return new GreaterThanEqualsFilter({ attribute, value: FilterParser._unescapeHexValues(remainingExpression.slice(2)), }); } if (remainingExpression.startsWith('<') && remainingExpression[1] === '=') { return new LessThanEqualsFilter({ attribute, value: FilterParser._unescapeHexValues(remainingExpression.slice(2)), }); } if (remainingExpression.startsWith('~') && remainingExpression[1] === '=') { return new ApproximateFilter({ attribute, value: FilterParser._unescapeHexValues(remainingExpression.slice(2)), }); } if (remainingExpression.startsWith(':')) { return FilterParser._parseExtensibleFilterFromString(attribute, remainingExpression); } throw new Error(`Invalid expression: ${filterString}`); } private static _parseExtensibleFilterFromString(attribute: string, filterString: string): ExtensibleFilter { let dnAttributes = false; let rule: string | undefined; const fields = filterString.split(':'); if (fields.length <= 1) { throw new Error(`Invalid extensible filter: ${filterString}`); } // Remove first entry, since it should be empty fields.shift(); if (fields[0]?.toLowerCase() === 'dn') { dnAttributes = true; fields.shift(); } if (fields.length && !fields[0]?.startsWith('=')) { rule = fields.shift(); } if (fields.length && !fields[0]?.startsWith('=')) { throw new Error(`Missing := in extensible filter: ${filterString}`); } // Trim the leading = (from the :=) and reinsert any extra ':' characters const remainingExpression = fields.join(':').slice(1); const options: ExtensibleFilterOptions = { matchType: attribute, dnAttributes, rule, value: FilterParser._unescapeHexValues(remainingExpression), }; // TODO: Enable this if it's useful // if (remainingExpression.indexOf('*') !== -1) { // const substring = FilterParser._escapeSubstring(remainingExpression); // options.initial = substring.initial; // options.any = substring.any; // options.final = substring.final; // } return new ExtensibleFilter(options); } private static _unescapeHexValues(input: string): string { let index = 0; const end = input.length; let result = ''; while (index < end) { const char = input[index]; switch (char) { case '(': throw new Error(`Illegal unescaped character: ${char} in value: ${input}`); case '\\': { const value = input.slice(index + 1, index + 3); if (/^[\dA-Fa-f]{2}$/.exec(value) === null) { throw new Error(`Invalid escaped hex character: ${value} in value: ${input}`); } result += String.fromCharCode(Number.parseInt(value, 16)); index += 3; break; } default: if (char) { result += char; } index += 1; break; } } return result; } private static _unescapeSubstring(input: string): Substring { const fields = input.split('*'); if (fields.length < 2) { throw new Error(`Wildcard missing: ${input}`); } return { initial: FilterParser._unescapeHexValues(fields.shift() ?? ''), final: FilterParser._unescapeHexValues(fields.pop() ?? ''), any: fields.map((field) => FilterParser._unescapeHexValues(field)), }; } private static _parseSet(reader: BerReader): Filter[] { const filters: Filter[] = []; const end = reader.offset + reader.length; while (reader.offset < end) { filters.push(FilterParser.parse(reader)); } return filters; } } ldapts-ldapts-91d5f4e/src/MessageParser.ts000066400000000000000000000123301522446626000206170ustar00rootroot00000000000000import { EventEmitter } from 'node:events'; import { type StrictEventEmitter } from 'strict-event-emitter-types'; import { BerReader } from './ber/index.js'; import { MessageParserError } from './errors/MessageParserError.js'; import { AddResponse, BindResponse, CompareResponse, DeleteResponse, ExtendedResponse, ModifyDNResponse, ModifyResponse, SearchEntry, SearchReference, SearchResponse } from './messages/index.js'; import { type Message } from './messages/Message.js'; import { type MessageResponse } from './messages/MessageResponse.js'; import { type ProtocolOperationValues } from './ProtocolOperation.js'; import { ProtocolOperation } from './ProtocolOperation.js'; interface MessageParserEvents { message: (message: MessageResponse) => void; error: (error: Error) => void; } type MessageParserEmitter = StrictEventEmitter; const TypedEventEmitter: new () => MessageParserEmitter = EventEmitter; export class MessageParser extends TypedEventEmitter { private buffer?: Buffer; public read(data: Buffer, messageDetailsByMessageId: Map): void { let nextMessage: Buffer | undefined; if (this.buffer) { this.buffer = Buffer.concat([this.buffer, data]); } else { this.buffer = data; } const reader = new BerReader(this.buffer); let foundSequence: number | null = null; try { foundSequence = reader.readSequence(); } catch (ex) { this.emit('error', ex as Error); } if (!foundSequence || reader.remain < reader.length) { // Have not received enough data to successfully parse return; } if (reader.remain > reader.length) { // Received too much data - extract next message and limit current reader nextMessage = this.buffer.subarray(reader.offset + reader.length); reader.setBufferSize(reader.offset + reader.length); } // Free up space since `ber` holds the current message and `nextMessage` is temporarily pointing // at the next sequence of data (if it exists) delete this.buffer; let messageId: number | null | undefined; let protocolOperation: number | null | undefined; try { messageId = reader.readInt(); if (messageId == null) { throw new Error(`Unable to read message id`); } protocolOperation = reader.readSequence(); if (protocolOperation == null) { throw new Error(`Unable to read protocol operation sequence for message: ${messageId}`); } const messageDetails = messageDetailsByMessageId.get(`${messageId}`); const message = this._getMessageFromProtocolOperation(messageId, protocolOperation, reader, messageDetails?.message); this.emit('message', message); } catch (ex) { if (messageId) { const errorWithMessageDetails = ex as MessageParserError; errorWithMessageDetails.messageDetails = { messageId, }; if (protocolOperation) { errorWithMessageDetails.messageDetails.protocolOperation = protocolOperation; } this.emit('error', errorWithMessageDetails); return; } this.emit('error', ex as Error); return; } if (nextMessage) { this.read(nextMessage, messageDetailsByMessageId); } } // eslint-disable-next-line @typescript-eslint/no-redundant-type-constituents private _getMessageFromProtocolOperation(messageId: number, protocolOperation: ProtocolOperationValues | number, reader: BerReader, messageDetails?: Message): MessageResponse { let message: MessageResponse; switch (protocolOperation) { case ProtocolOperation.LDAP_RES_BIND: message = new BindResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_ADD: message = new AddResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_COMPARE: message = new CompareResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_DELETE: message = new DeleteResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_EXTENSION: message = new ExtendedResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_MODRDN: message = new ModifyDNResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_MODIFY: message = new ModifyResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_SEARCH: message = new SearchResponse({ messageId, }); break; case ProtocolOperation.LDAP_RES_SEARCH_ENTRY: message = new SearchEntry({ messageId, }); break; case ProtocolOperation.LDAP_RES_SEARCH_REF: message = new SearchReference({ messageId, }); break; default: { const error = new MessageParserError(`Protocol Operation not supported: 0x${protocolOperation.toString(16)}`); error.messageDetails = { messageId, protocolOperation, }; throw error; } } message.parse(reader, messageDetails?.controls ?? []); return message; } } ldapts-ldapts-91d5f4e/src/MessageResponseStatus.ts000066400000000000000000000001421522446626000223630ustar00rootroot00000000000000export const MessageResponseStatus = { Success: 0 as const, SizeLimitExceeded: 4 as const, }; ldapts-ldapts-91d5f4e/src/PostalAddress.ts000066400000000000000000000017631522446626000206360ustar00rootroot00000000000000/** * Encodes a postal address string into RFC 4517 Postal Address Syntax. * @param {string} address - The address with lines separated by the separator * @param {string} separator - Line separator in the input (default: '\n') * @returns {string} Encoded postal address string */ export function encodePostalAddress(address: string, separator = '\n'): string { return address .split(separator) .map((line) => line.replaceAll('\\', '\\5C').replaceAll('$', '\\24')) .join('$'); } /** * Decodes an RFC 4517 Postal Address Syntax string into a postal address. * @param {string} encoded - The RFC 4517 encoded postal address * @param {string} separator - Line separator for the output (default: '\n') * @returns {string} Decoded postal address string */ export function decodePostalAddress(encoded: string, separator = '\n'): string { return encoded .split('$') .map((line) => line.replace(/\\(5c|24)/gi, (_, code: string) => (code === '24' ? '$' : '\\'))) .join(separator); } ldapts-ldapts-91d5f4e/src/ProtocolOperation.ts000066400000000000000000000017441522446626000215470ustar00rootroot00000000000000export const ProtocolOperation = { // Misc LDAP_VERSION_3: 0x03 as const, LBER_SET: 0x31 as const, LDAP_CONTROLS: 0xa0 as const, // Requests LDAP_REQ_BIND: 0x60 as const, LDAP_REQ_BIND_SASL: 0xa3 as const, LDAP_REQ_UNBIND: 0x42 as const, LDAP_REQ_SEARCH: 0x63 as const, LDAP_REQ_MODIFY: 0x66 as const, LDAP_REQ_ADD: 0x68 as const, LDAP_REQ_DELETE: 0x4a as const, LDAP_REQ_MODRDN: 0x6c as const, LDAP_REQ_COMPARE: 0x6e as const, LDAP_REQ_ABANDON: 0x50 as const, LDAP_REQ_EXTENSION: 0x77 as const, // Responses LDAP_RES_BIND: 0x61 as const, LDAP_RES_SEARCH_ENTRY: 0x64 as const, LDAP_RES_SEARCH_REF: 0x73 as const, LDAP_RES_SEARCH: 0x65 as const, LDAP_RES_MODIFY: 0x67 as const, LDAP_RES_ADD: 0x69 as const, LDAP_RES_DELETE: 0x6b as const, LDAP_RES_MODRDN: 0x6d as const, LDAP_RES_COMPARE: 0x6f as const, LDAP_RES_EXTENSION: 0x78 as const, }; export type ProtocolOperationValues = (typeof ProtocolOperation)[keyof typeof ProtocolOperation]; ldapts-ldapts-91d5f4e/src/SearchFilter.ts000066400000000000000000000006141522446626000204330ustar00rootroot00000000000000export const SearchFilter = { and: 0xa0 as const, or: 0xa1 as const, not: 0xa2 as const, equalityMatch: 0xa3 as const, substrings: 0xa4 as const, greaterOrEqual: 0xa5 as const, lessOrEqual: 0xa6 as const, present: 0x87 as const, approxMatch: 0xa8 as const, extensibleMatch: 0xa9 as const, }; export type SearchFilterValues = (typeof SearchFilter)[keyof typeof SearchFilter]; ldapts-ldapts-91d5f4e/src/StatusCodeParser.ts000066400000000000000000000107751522446626000213240ustar00rootroot00000000000000import { type ResultCodeError } from './errors/index.js'; import { AdminLimitExceededError, AffectsMultipleDSAsError, AliasDerefProblemError, AliasProblemError, AlreadyExistsError, AuthMethodNotSupportedError, BusyError, ConfidentialityRequiredError, ConstraintViolationError, InappropriateAuthError, InappropriateMatchingError, InsufficientAccessError, InvalidCredentialsError, InvalidDNSyntaxError, InvalidSyntaxError, IsLeafError, LoopDetectError, MoreResultsToReturnError, NamingViolationError, NoObjectClassModsError, NoResultError, NoSuchAttributeError, NoSuchObjectError, NotAllowedOnNonLeafError, NotAllowedOnRDNError, ObjectClassViolationError, OperationsError, ProtocolError, ResultsTooLargeError, SaslBindInProgressError, SizeLimitExceededError, StrongAuthRequiredError, TimeLimitExceededError, TLSNotSupportedError, TypeOrValueExistsError, UnavailableCriticalExtensionError, UnavailableError, UndefinedTypeError, UnknownStatusCodeError, UnwillingToPerformError, } from './errors/index.js'; import { type BindResponse } from './messages/BindResponse.js'; import { type MessageResponse } from './messages/MessageResponse.js'; // eslint-disable-next-line @typescript-eslint/no-extraneous-class export class StatusCodeParser { public static parse(result?: MessageResponse): ResultCodeError { if (!result) { return new NoResultError(); } switch (result.status) { case 1: return new OperationsError(result.errorMessage); case 2: return new ProtocolError(result.errorMessage); case 3: return new TimeLimitExceededError(result.errorMessage); case 4: return new SizeLimitExceededError(result.errorMessage); case 7: return new AuthMethodNotSupportedError(result.errorMessage); case 8: return new StrongAuthRequiredError(result.errorMessage); case 11: return new AdminLimitExceededError(result.errorMessage); case 12: return new UnavailableCriticalExtensionError(result.errorMessage); case 13: return new ConfidentialityRequiredError(result.errorMessage); case 14: return new SaslBindInProgressError(result as BindResponse); case 16: return new NoSuchAttributeError(result.errorMessage); case 17: return new UndefinedTypeError(result.errorMessage); case 18: return new InappropriateMatchingError(result.errorMessage); case 19: return new ConstraintViolationError(result.errorMessage); case 20: return new TypeOrValueExistsError(result.errorMessage); case 21: return new InvalidSyntaxError(result.errorMessage); case 32: return new NoSuchObjectError(result.errorMessage); case 33: return new AliasProblemError(result.errorMessage); case 34: return new InvalidDNSyntaxError(result.errorMessage); case 35: return new IsLeafError(result.errorMessage); case 36: return new AliasDerefProblemError(result.errorMessage); case 48: return new InappropriateAuthError(result.errorMessage); case 49: return new InvalidCredentialsError(result.errorMessage); case 50: return new InsufficientAccessError(result.errorMessage); case 51: return new BusyError(result.errorMessage); case 52: return new UnavailableError(result.errorMessage); case 53: return new UnwillingToPerformError(result.errorMessage); case 54: return new LoopDetectError(result.errorMessage); case 64: return new NamingViolationError(result.errorMessage); case 65: return new ObjectClassViolationError(result.errorMessage); case 66: return new NotAllowedOnNonLeafError(result.errorMessage); case 67: return new NotAllowedOnRDNError(result.errorMessage); case 68: return new AlreadyExistsError(result.errorMessage); case 69: return new NoObjectClassModsError(result.errorMessage); case 70: return new ResultsTooLargeError(result.errorMessage); case 71: return new AffectsMultipleDSAsError(result.errorMessage); case 95: return new MoreResultsToReturnError(result.errorMessage); case 112: return new TLSNotSupportedError(result.errorMessage); case 248: return new NoResultError(result.errorMessage); default: return new UnknownStatusCodeError(result.status, result.errorMessage); } } } ldapts-ldapts-91d5f4e/src/ber/000077500000000000000000000000001522446626000162575ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/ber/Ber.ts000066400000000000000000000011571522446626000173430ustar00rootroot00000000000000export const Ber = { EOC: 0, Boolean: 1, Integer: 2, BitString: 3, OctetString: 4, Null: 5, OID: 6, ObjectDescriptor: 7, External: 8, Real: 9, Enumeration: 10, PDV: 11, Utf8String: 12, RelativeOID: 13, Sequence: 16, Set: 17, NumericString: 18, PrintableString: 19, T61String: 20, VideotexString: 21, IA5String: 22, UTCTime: 23, GeneralizedTime: 24, GraphicString: 25, VisibleString: 26, GeneralString: 28, UniversalString: 29, CharacterString: 30, BMPString: 31, Constructor: 32, Context: 128, } as const; export type BerType = (typeof Ber)[keyof typeof Ber]; ldapts-ldapts-91d5f4e/src/ber/BerReader.ts000066400000000000000000000125711522446626000204700ustar00rootroot00000000000000/* eslint-disable no-bitwise */ import { Ber } from './Ber.js'; import { InvalidAsn1Error } from './InvalidAsn1Error.js'; export class BerReader { private size: number; private currentLength = 0; private currentOffset = 0; public readonly buffer: Buffer; public constructor(data: Buffer) { if (!Buffer.isBuffer(data)) { throw new TypeError('data must be a Buffer'); } this.buffer = data; this.size = data.length; } public get length(): number { return this.currentLength; } public get offset(): number { return this.currentOffset; } public set offset(value: number) { this.currentOffset = value; } public get remain(): number { return this.size - this.currentOffset; } public get remainingBuffer(): Buffer { return this.buffer.subarray(this.currentOffset); } public setBufferSize(size: number): void { this.size = size; } public readByte(peek = false): number | null { if (this.size - this.currentOffset < 1) { return null; } const byte = this.buffer.readUInt8(this.currentOffset); if (!peek) { this.currentOffset += 1; } return byte; } public peek(): number | null { return this.readByte(true); } public readLength(startOffset?: number): number | null { let offset = startOffset ?? this.currentOffset; if (offset >= this.size) { return null; } const lengthByte = this.buffer.readUInt8(offset); offset += 1; if ((lengthByte & 0x80) === 0x80) { const numLengthBytes = lengthByte & 0x7f; if (numLengthBytes === 0) { throw new InvalidAsn1Error('Indefinite length not supported'); } if (numLengthBytes > 4) { throw new InvalidAsn1Error('Encoding too long'); } if (this.size - offset < numLengthBytes) { return null; } this.currentLength = 0; for (let i = 0; i < numLengthBytes; i++) { this.currentLength = (this.currentLength << 8) + this.buffer.readUInt8(offset); offset += 1; } } else { this.currentLength = lengthByte; } return offset; } public readSequence(expectedTag?: number): number | null { const tag = this.peek(); if (tag === null) { return null; } if (expectedTag !== undefined && expectedTag !== tag) { throw new InvalidAsn1Error(`Expected 0x${expectedTag.toString(16)}: got 0x${tag.toString(16)}`); } const newOffset = this.readLength(this.currentOffset + 1); if (newOffset === null) { return null; } this.currentOffset = newOffset; return tag; } public readInt(): number | null { return this.readTag(Ber.Integer); } public readBoolean(): boolean | null { const value = this.readTag(Ber.Boolean); if (value === null) { return null; } return value !== 0; } public readEnumeration(): number | null { return this.readTag(Ber.Enumeration); } public readString(tag?: number, asBuffer?: false): string | null; public readString(tag: number, asBuffer: true): Buffer | null; public readString(tag: number = Ber.OctetString, asBuffer = false): Buffer | string | null { const currentTag = this.peek(); if (currentTag === null) { return null; } if (currentTag !== tag) { throw new InvalidAsn1Error(`Expected 0x${tag.toString(16)}: got 0x${currentTag.toString(16)}`); } const newOffset = this.readLength(this.currentOffset + 1); if (newOffset === null) { return null; } if (this.currentLength > this.size - newOffset) { return null; } this.currentOffset = newOffset; if (this.currentLength === 0) { return asBuffer ? Buffer.alloc(0) : ''; } const value = this.buffer.subarray(this.currentOffset, this.currentOffset + this.currentLength); this.currentOffset += this.currentLength; return asBuffer ? value : value.toString('utf8'); } public readOID(tag: number = Ber.OID): string | null { const data = this.readString(tag, true); if (data === null) { return null; } const values: number[] = []; let value = 0; for (const byte of data) { value = (value << 7) + (byte & 0x7f); if ((byte & 0x80) === 0) { values.push(value); value = 0; } } const firstValue = values[0] ?? 0; return [Math.trunc(firstValue / 40), firstValue % 40, ...values.slice(1)].join('.'); } public readTag(tag: number): number | null { const currentTag = this.peek(); if (currentTag === null) { return null; } if (currentTag !== tag) { throw new InvalidAsn1Error(`Expected 0x${tag.toString(16)}: got 0x${currentTag.toString(16)}`); } const newOffset = this.readLength(this.currentOffset + 1); if (newOffset === null) { return null; } if (this.currentLength > 4) { throw new InvalidAsn1Error(`Integer too long: ${this.currentLength}`); } if (this.currentLength > this.size - newOffset) { return null; } this.currentOffset = newOffset; const firstByte = this.buffer.readUInt8(this.currentOffset); let value = 0; for (let i = 0; i < this.currentLength; i++) { value = (value << 8) | this.buffer.readUInt8(this.currentOffset); this.currentOffset += 1; } if ((firstByte & 0x80) === 0x80 && this.currentLength < 4) { value -= 1 << (this.currentLength * 8); } return value >> 0; } } ldapts-ldapts-91d5f4e/src/ber/BerWriter.ts000066400000000000000000000160271522446626000205420ustar00rootroot00000000000000/* eslint-disable no-bitwise */ import { Ber } from './Ber.js'; import { InvalidAsn1Error } from './InvalidAsn1Error.js'; export interface BerWriterOptions { size?: number; growthFactor?: number; } export class BerWriter { private data: Buffer; private size: number; private currentOffset = 0; private readonly growthFactor: number; private readonly sequenceOffsets: number[] = []; public constructor(options: BerWriterOptions = {}) { const initialSize = options.size ?? 1024; this.growthFactor = options.growthFactor ?? 8; this.data = Buffer.alloc(initialSize); this.size = initialSize; } public get buffer(): Buffer { if (this.sequenceOffsets.length > 0) { throw new InvalidAsn1Error(`${this.sequenceOffsets.length} unended sequence(s)`); } return this.data.subarray(0, this.currentOffset); } public writeByte(value: number): void { this.ensureCapacity(1); this.data[this.currentOffset++] = value; } public writeInt(value: number, tag: number = Ber.Integer): void { let intValue = value; let byteCount = 4; while (((intValue & 0xff800000) === 0 || (intValue & 0xff800000) === 0xff800000 >> 0) && byteCount > 1) { byteCount--; intValue <<= 8; } if (byteCount > 4) { throw new InvalidAsn1Error('BER integers cannot be > 0xffffffff'); } this.ensureCapacity(2 + byteCount); this.data[this.currentOffset++] = tag; this.data[this.currentOffset++] = byteCount; while (byteCount > 0) { byteCount--; this.data[this.currentOffset++] = (intValue & 0xff000000) >>> 24; intValue <<= 8; } } public writeNull(): void { this.writeByte(Ber.Null); this.writeByte(0x00); } public writeEnumeration(value: number, tag: number = Ber.Enumeration): void { this.writeInt(value, tag); } public writeBoolean(value: boolean, tag: number = Ber.Boolean): void { this.ensureCapacity(3); this.data[this.currentOffset++] = tag; this.data[this.currentOffset++] = 0x01; this.data[this.currentOffset++] = value ? 0xff : 0x00; } public writeString(value: string, tag: number = Ber.OctetString): void { const byteLength = Buffer.byteLength(value); this.writeByte(tag); this.writeLength(byteLength); if (byteLength > 0) { this.ensureCapacity(byteLength); this.data.write(value, this.currentOffset); this.currentOffset += byteLength; } } public writeBuffer(value: Buffer, tag: number): void { this.writeByte(tag); this.writeLength(value.length); this.ensureCapacity(value.length); value.copy(this.data, this.currentOffset, 0, value.length); this.currentOffset += value.length; } public writeStringArray(values: string[]): void { for (const value of values) { this.writeString(value); } } public writeOID(value: string, tag: number = Ber.OID): void { // eslint-disable-next-line security/detect-unsafe-regex if (!/^(\d+\.){3,}\d+$/.test(value)) { throw new Error('Argument is not a valid OID string'); } const parts = value.split('.'); const bytes: number[] = []; const firstPart = parts[0] ?? '0'; const secondPart = parts[1] ?? '0'; bytes.push(Number(firstPart) * 40 + Number(secondPart)); for (const part of parts.slice(2)) { this.encodeOidOctet(bytes, Number(part)); } this.ensureCapacity(2 + bytes.length); this.writeByte(tag); this.writeLength(bytes.length); for (const byte of bytes) { this.writeByte(byte); } } public writeLength(length: number): void { this.ensureCapacity(4); if (length <= 0x7f) { this.data[this.currentOffset++] = length; } else if (length <= 0xff) { this.data[this.currentOffset++] = 0x81; this.data[this.currentOffset++] = length; } else if (length <= 0xffff) { this.data[this.currentOffset++] = 0x82; this.data[this.currentOffset++] = length >> 8; this.data[this.currentOffset++] = length; } else if (length <= 0xffffff) { this.data[this.currentOffset++] = 0x83; this.data[this.currentOffset++] = length >> 16; this.data[this.currentOffset++] = length >> 8; this.data[this.currentOffset++] = length; } else { throw new InvalidAsn1Error('Length too long (> 4 bytes)'); } } public startSequence(tag: number = Ber.Sequence | Ber.Constructor): void { this.writeByte(tag); this.sequenceOffsets.push(this.currentOffset); this.ensureCapacity(3); this.currentOffset += 3; } public endSequence(): void { const sequenceStart = this.sequenceOffsets.pop(); if (sequenceStart === undefined) { throw new InvalidAsn1Error('No sequence to end'); } const contentStart = sequenceStart + 3; const contentLength = this.currentOffset - contentStart; if (contentLength <= 0x7f) { this.shiftContent(contentStart, contentLength, -2); this.data[sequenceStart] = contentLength; } else if (contentLength <= 0xff) { this.shiftContent(contentStart, contentLength, -1); this.data[sequenceStart] = 0x81; this.data[sequenceStart + 1] = contentLength; } else if (contentLength <= 0xffff) { this.data[sequenceStart] = 0x82; this.data[sequenceStart + 1] = contentLength >> 8; this.data[sequenceStart + 2] = contentLength; } else if (contentLength <= 0xffffff) { this.shiftContent(contentStart, contentLength, 1); this.data[sequenceStart] = 0x83; this.data[sequenceStart + 1] = contentLength >> 16; this.data[sequenceStart + 2] = contentLength >> 8; this.data[sequenceStart + 3] = contentLength; } else { throw new InvalidAsn1Error('Sequence too long'); } } private encodeOidOctet(bytes: number[], octet: number): void { if (octet < 128) { bytes.push(octet); } else if (octet < 16384) { bytes.push((octet >>> 7) | 0x80); bytes.push(octet & 0x7f); } else if (octet < 2097152) { bytes.push((octet >>> 14) | 0x80); bytes.push(((octet >>> 7) | 0x80) & 0xff); bytes.push(octet & 0x7f); } else if (octet < 268435456) { bytes.push((octet >>> 21) | 0x80); bytes.push(((octet >>> 14) | 0x80) & 0xff); bytes.push(((octet >>> 7) | 0x80) & 0xff); bytes.push(octet & 0x7f); } else { bytes.push(((octet >>> 28) | 0x80) & 0xff); bytes.push(((octet >>> 21) | 0x80) & 0xff); bytes.push(((octet >>> 14) | 0x80) & 0xff); bytes.push(((octet >>> 7) | 0x80) & 0xff); bytes.push(octet & 0x7f); } } private shiftContent(start: number, length: number, shift: number): void { this.data.copy(this.data, start + shift, start, start + length); this.currentOffset += shift; } private ensureCapacity(needed: number): void { if (this.size - this.currentOffset < needed) { let newSize = this.size * this.growthFactor; if (newSize - this.currentOffset < needed) { newSize += needed; } const newBuffer = Buffer.alloc(newSize); this.data.copy(newBuffer, 0, 0, this.currentOffset); this.data = newBuffer; this.size = newSize; } } } ldapts-ldapts-91d5f4e/src/ber/InvalidAsn1Error.ts000066400000000000000000000002241522446626000217500ustar00rootroot00000000000000export class InvalidAsn1Error extends Error { public constructor(message: string) { super(message); this.name = 'InvalidAsn1Error'; } } ldapts-ldapts-91d5f4e/src/ber/index.ts000066400000000000000000000002011522446626000177270ustar00rootroot00000000000000export * from './BerReader.js'; export * from './Ber.js'; export * from './BerWriter.js'; export * from './InvalidAsn1Error.js'; ldapts-ldapts-91d5f4e/src/controls/000077500000000000000000000000001522446626000173525ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/controls/Control.ts000066400000000000000000000016551522446626000213510ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; export interface ControlOptions { critical?: boolean; } export class Control { public type: string; public critical: boolean; public constructor(type: string, options: ControlOptions = {}) { this.type = type; this.critical = options.critical === true; } public write(writer: BerWriter): void { writer.startSequence(); writer.writeString(this.type); writer.writeBoolean(this.critical); this.writeControl(writer); writer.endSequence(); } public parse(reader: BerReader): void { this.parseControl(reader); } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected writeControl(_: BerWriter): void { // Do nothing as the default action } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected parseControl(_: BerReader): void { // Do nothing as the default action } } ldapts-ldapts-91d5f4e/src/controls/EntryChangeNotificationControl.ts000066400000000000000000000032301522446626000260370ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { BerWriter } from '../ber/index.js'; import { type ControlOptions } from './Control.js'; import { Control } from './Control.js'; export interface EntryChangeNotificationControlValue { changeType: number; previousDN?: string | null; changeNumber: number; } export interface EntryChangeNotificationControlOptions extends ControlOptions { value?: EntryChangeNotificationControlValue; } export class EntryChangeNotificationControl extends Control { public static type = '2.16.840.1.113730.3.4.7'; public value?: EntryChangeNotificationControlValue; public constructor(options: EntryChangeNotificationControlOptions = {}) { super(EntryChangeNotificationControl.type, options); this.value = options.value; } public override parseControl(reader: BerReader): void { if (reader.readSequence()) { const changeType = reader.readInt() ?? 0; let previousDN: string | null | undefined; if (changeType === 8) { previousDN = reader.readString(); } const changeNumber = reader.readInt() ?? 0; this.value = { changeType, previousDN, changeNumber, }; } } public override writeControl(writer: BerWriter): void { if (!this.value) { return; } const controlWriter = new BerWriter(); controlWriter.startSequence(); controlWriter.writeInt(this.value.changeType); if (this.value.previousDN) { controlWriter.writeString(this.value.previousDN); } controlWriter.writeInt(this.value.changeNumber); controlWriter.endSequence(); writer.writeBuffer(controlWriter.buffer, 0x04); } } ldapts-ldapts-91d5f4e/src/controls/PagedResultsControl.ts000066400000000000000000000026201522446626000236650ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { Ber, BerWriter } from '../ber/index.js'; import { type ControlOptions } from './Control.js'; import { Control } from './Control.js'; export interface PagedResultsValue { size: number; cookie?: Buffer; } export interface PagedResultsControlOptions extends ControlOptions { value?: PagedResultsValue; } export class PagedResultsControl extends Control { public static type = '1.2.840.113556.1.4.319'; public value?: PagedResultsValue; public constructor(options: PagedResultsControlOptions = {}) { super(PagedResultsControl.type, options); this.value = options.value; } public override parseControl(reader: BerReader): void { if (reader.readSequence()) { const size = reader.readInt() ?? 0; const cookie = reader.readString(Ber.OctetString, true) ?? Buffer.alloc(0); this.value = { size, cookie, }; } } public override writeControl(writer: BerWriter): void { if (!this.value) { return; } const controlWriter = new BerWriter(); controlWriter.startSequence(); controlWriter.writeInt(this.value.size); if (this.value.cookie?.length) { controlWriter.writeBuffer(this.value.cookie, Ber.OctetString); } else { controlWriter.writeString(''); } controlWriter.endSequence(); writer.writeBuffer(controlWriter.buffer, 0x04); } } ldapts-ldapts-91d5f4e/src/controls/PersistentSearchControl.ts000066400000000000000000000027401522446626000245540ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { BerWriter } from '../ber/index.js'; import { type ControlOptions } from './Control.js'; import { Control } from './Control.js'; export interface PersistentSearchValue { changeTypes: number; changesOnly: boolean; returnECs: boolean; } export interface PersistentSearchControlOptions extends ControlOptions { value?: PersistentSearchValue; } export class PersistentSearchControl extends Control { public static type = '2.16.840.1.113730.3.4.3'; public value?: PersistentSearchValue; public constructor(options: PersistentSearchControlOptions = {}) { super(PersistentSearchControl.type, options); this.value = options.value; } public override parseControl(reader: BerReader): void { if (reader.readSequence()) { const changeTypes = reader.readInt() ?? 0; const changesOnly = reader.readBoolean() ?? false; const returnECs = reader.readBoolean() ?? false; this.value = { changeTypes, changesOnly, returnECs, }; } } public override writeControl(writer: BerWriter): void { if (!this.value) { return; } const controlWriter = new BerWriter(); controlWriter.startSequence(); controlWriter.writeInt(this.value.changeTypes); controlWriter.writeBoolean(this.value.changesOnly); controlWriter.writeBoolean(this.value.returnECs); controlWriter.endSequence(); writer.writeBuffer(controlWriter.buffer, 0x04); } } ldapts-ldapts-91d5f4e/src/controls/ServerSideSortingRequestControl.ts000066400000000000000000000043671522446626000262670ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { Ber, BerWriter } from '../ber/index.js'; import { type ControlOptions } from './Control.js'; import { Control } from './Control.js'; export interface ServerSideSortingRequestValue { attributeType: string; orderingRule?: string; reverseOrder?: boolean; } export interface ServerSideSortingRequestControlOptions extends ControlOptions { value?: ServerSideSortingRequestValue | ServerSideSortingRequestValue[]; } export class ServerSideSortingRequestControl extends Control { public static type = '1.2.840.113556.1.4.473'; public values: ServerSideSortingRequestValue[]; public constructor(options: ServerSideSortingRequestControlOptions = {}) { super(ServerSideSortingRequestControl.type, options); if (Array.isArray(options.value)) { this.values = options.value; } else if (typeof options.value === 'object') { this.values = [options.value]; } else { this.values = []; } } public override parseControl(reader: BerReader): void { if (reader.readSequence(0x30)) { while (reader.readSequence(0x30)) { const attributeType = reader.readString() ?? ''; let orderingRule = ''; let reverseOrder = false; if (reader.peek() === 0x80) { orderingRule = reader.readString(0x80) ?? ''; } if (reader.peek() === 0x81) { reverseOrder = reader.readTag(0x81) !== 0; } this.values.push({ attributeType, orderingRule, reverseOrder, }); } } } public override writeControl(writer: BerWriter): void { if (!this.values.length) { return; } const controlWriter = new BerWriter(); controlWriter.startSequence(0x30); for (const value of this.values) { controlWriter.startSequence(0x30); controlWriter.writeString(value.attributeType, Ber.OctetString); if (value.orderingRule) { controlWriter.writeString(value.orderingRule, 0x80); } if (typeof value.reverseOrder !== 'undefined') { controlWriter.writeBoolean(value.reverseOrder, 0x81); } controlWriter.endSequence(); } controlWriter.endSequence(); writer.writeBuffer(controlWriter.buffer, 0x04); } } ldapts-ldapts-91d5f4e/src/controls/index.ts000066400000000000000000000003411522446626000210270ustar00rootroot00000000000000export * from './Control.js'; export * from './EntryChangeNotificationControl.js'; export * from './PagedResultsControl.js'; export * from './PersistentSearchControl.js'; export * from './ServerSideSortingRequestControl.js'; ldapts-ldapts-91d5f4e/src/dn/000077500000000000000000000000001522446626000161105ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/dn/DN.ts000066400000000000000000000076751522446626000170000ustar00rootroot00000000000000import { type RDNAttributes } from './RDN.js'; import { RDN } from './RDN.js'; /** * RDNMap is an interface, that maps every key & value to a specified RDN. * * Value can be either a string or a list of strings, where every value in the list will * get applied to the same key of an RDN. */ export type RDNMap = Record; /** * DN class provides chain building of multiple RDNs, which can be later build into * escaped string representation. */ export class DN { private rdns: RDN[] = []; public constructor(rdns?: RDN[] | RDNMap) { if (rdns) { if (Array.isArray(rdns)) { this.rdns = rdns; } else { this.addRDNs(rdns); } } } /** * Add an RDN component to the DN, consisting of key & value pair. * @param {string} key * @param {string} value * @returns {object} DN */ public addPairRDN(key: string, value: string): this { this.rdns.push(new RDN({ [key]: value })); return this; } /** * Add a single RDN component to the DN. * * Note, that this RDN can be compound (single RDN can have multiple key & value pairs). * @param {object} rdn * @returns {object} DN */ public addRDN(rdn: RDN | RDNAttributes): this { if (rdn instanceof RDN) { this.rdns.push(rdn); } else { this.rdns.push(new RDN(rdn)); } return this; } /** * Add multiple RDN components to the DN. * * This method allows different interfaces to add RDNs into the DN. * It can: * - join other DN into this DN * - join list of RDNs or RDNAttributes into this DN * - create RDNs from object map, where every key & value will create a new RDN * @param {object|object[]} rdns * @returns {object} DN */ public addRDNs(rdns: DN | RDN[] | RDNAttributes[] | RDNMap): this { if (rdns instanceof DN) { this.rdns.push(...rdns.rdns); } else if (Array.isArray(rdns)) { for (const rdn of rdns) { this.addRDN(rdn); } } else { for (const [name, value] of Object.entries(rdns)) { if (Array.isArray(value)) { for (const rdnValue of value) { this.rdns.push( new RDN({ [name]: rdnValue, }), ); } } else { this.rdns.push( new RDN({ [name]: value, }), ); } } } return this; } public getRDNs(): RDN[] { return this.rdns; } public get(index: number): RDN | undefined { return this.rdns[index]; } public set(rdn: RDN | RDNAttributes, index: number): this { if (rdn instanceof RDN) { this.rdns[index] = rdn; } else { this.rdns[index] = new RDN(rdn); } return this; } public isEmpty(): boolean { return !this.rdns.length; } /** * Checks, if this instance of DN is equal to the other DN. * @param {object} other * @returns true if equal; otherwise false */ public equals(other: DN): boolean { if (this.rdns.length !== other.rdns.length) { return false; } for (let i = 0; i < this.rdns.length; i += 1) { const rdn = this.rdns[i]; const otherRdn = other.rdns[i]; if (rdn == null && otherRdn == null) { continue; } if (rdn == null || otherRdn == null || !rdn.equals(otherRdn)) { return false; } } return true; } public clone(): DN { return new DN([...this.rdns]); } public reverse(): this { this.rdns.reverse(); return this; } public pop(): RDN | undefined { return this.rdns.pop(); } public shift(): RDN | undefined { return this.rdns.shift(); } /** * Parse the DN, escape values & return a string representation. * @returns String representation of DN */ public toString(): string { let str = ''; for (const rdn of this.rdns) { if (str.length) { str += ','; } str += rdn.toString(); } return str; } } ldapts-ldapts-91d5f4e/src/dn/RDN.ts000066400000000000000000000066101522446626000171060ustar00rootroot00000000000000export type RDNAttributes = Record; /** * RDN is a part of DN, and it consists of key & value pair. This class also supports * compound RDNs, meaning that one RDN can hold multiple key & value pairs. */ export class RDN { private attrs: RDNAttributes = {}; public constructor(attrs?: RDNAttributes) { if (attrs) { for (const [key, value] of Object.entries(attrs)) { this.set(key, value); } } } /** * Set an RDN pair. * @param {string} name * @param {string} value * @returns {object} RDN class */ public set(name: string, value: string): this { this.attrs[name] = value; return this; } /** * Get an RDN value at the specified name. * @param {string} name * @returns {string | undefined} value */ public get(name: string): string | undefined { return this.attrs[name]; } /** * Checks, if this instance of RDN is equal to the other RDN. * @param {object} other * @returns true if equal; otherwise false */ public equals(other: RDN): boolean { const ourKeys = Object.keys(this.attrs); const otherKeys = Object.keys(other.attrs); if (ourKeys.length !== otherKeys.length) { return false; } ourKeys.sort(); otherKeys.sort(); for (const [i, key] of ourKeys.entries()) { // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition if (key == null || key !== otherKeys[i]) { return false; } const ourValue = this.attrs[key]; const otherValue = other.attrs[key]; if (ourValue == null && otherValue == null) { continue; } if (ourValue == null || otherValue == null || ourValue !== otherValue) { return false; } } return true; } /** * Parse the RDN, escape values & return a string representation. * @returns {string} Escaped string representation of RDN. */ public toString(): string { let str = ''; for (const [key, value] of Object.entries(this.attrs)) { if (str) { str += '+'; } str += `${key}=${this._escape(value)}`; } return str; } /** * Escape values & return a string representation. * * RFC defines, that these characters should be escaped: * * Comma , * Backslash character \ * Pound sign (hash sign) # * Plus sign + * Less than symbol < * Greater than symbol > * Semicolon ; * Double quote (quotation mark) " * Equal sign = * Leading or trailing spaces * @param {string} value - RDN value to be escaped * @returns {string} Escaped string representation of RDN */ private _escape(value = ''): string { let str = ''; let current = 0; let quoted = false; const len = value.length; const escaped = /["\\]/; const special = /[#+,;<=>]/; if (len > 0) { // Wrap strings with trailing or leading spaces in quotes quoted = value.startsWith(' ') || value[len - 1] === ' '; } while (current < len) { const character = value[current] ?? ''; if (escaped.test(character) || (!quoted && special.test(character))) { str += '\\'; } if (character) { str += character; } current += 1; } if (quoted) { str = `"${str}"`; } return str; } } ldapts-ldapts-91d5f4e/src/dn/index.ts000066400000000000000000000000631522446626000175660ustar00rootroot00000000000000export * from './DN.js'; export * from './RDN.js'; ldapts-ldapts-91d5f4e/src/errors/000077500000000000000000000000001522446626000170235ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/errors/MessageParserError.ts000066400000000000000000000005611522446626000231500ustar00rootroot00000000000000export interface MessageParserErrorDetails { messageId: number; protocolOperation?: number; } export class MessageParserError extends Error { public messageDetails?: MessageParserErrorDetails; public constructor(message: string) { super(message); this.name = 'MessageParserError'; Object.setPrototypeOf(this, MessageParserError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/index.ts000066400000000000000000000001261522446626000205010ustar00rootroot00000000000000export * from './MessageParserError.js'; export * from './resultCodeErrors/index.js'; ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/000077500000000000000000000000001522446626000223315ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AdminLimitExceededError.ts000066400000000000000000000005771522446626000274020ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AdminLimitExceededError extends ResultCodeError { public constructor(message?: string) { super(11, message ?? 'An LDAP server limit set by an administrative authority has been exceeded.'); this.name = 'AdminLimitExceededError'; Object.setPrototypeOf(this, AdminLimitExceededError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AffectsMultipleDSAsError.ts000066400000000000000000000006541522446626000275220ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AffectsMultipleDSAsError extends ResultCodeError { public constructor(message?: string) { super(71, message ?? 'The modify DN operation moves the entry from one LDAP server to another and thus requires more than one LDAP server.'); this.name = 'AffectsMultipleDSAsError'; Object.setPrototypeOf(this, AffectsMultipleDSAsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AliasDerefProblemError.ts000066400000000000000000000006421522446626000272350ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AliasDerefProblemError extends ResultCodeError { public constructor(message?: string) { super(36, message ?? "Either the client does not have access rights to read the aliased object's name or dereferencing is not allowed."); this.name = 'AliasDerefProblemError'; Object.setPrototypeOf(this, AliasDerefProblemError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AliasProblemError.ts000066400000000000000000000005241522446626000262660ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AliasProblemError extends ResultCodeError { public constructor(message?: string) { super(33, message ?? 'An error occurred when an alias was dereferenced.'); this.name = 'AliasProblemError'; Object.setPrototypeOf(this, AliasProblemError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AlreadyExistsError.ts000066400000000000000000000007151522446626000264770ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AlreadyExistsError extends ResultCodeError { public constructor(message?: string) { super(68, message ?? 'The add operation attempted to add an entry that already exists, or that the modify operation attempted to rename an entry to the name of an entry that already exists.'); this.name = 'AlreadyExistsError'; Object.setPrototypeOf(this, AlreadyExistsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/AuthMethodNotSupportedError.ts000066400000000000000000000006121522446626000303430ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class AuthMethodNotSupportedError extends ResultCodeError { public constructor(message?: string) { super(7, message ?? 'The Directory Server does not support the requested Authentication Method.'); this.name = 'AuthMethodNotSupportedError'; Object.setPrototypeOf(this, AuthMethodNotSupportedError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/BusyError.ts000066400000000000000000000005221522446626000246340ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class BusyError extends ResultCodeError { public constructor(message?: string) { super(51, message ?? 'The LDAP server is too busy to process the client request at this time.'); this.name = 'BusyError'; Object.setPrototypeOf(this, BusyError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ConfidentialityRequiredError.ts000066400000000000000000000010351522446626000305400ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class ConfidentialityRequiredError extends ResultCodeError { public constructor(message?: string) { super( 13, message ?? 'The session is not protected by a protocol such as Transport Layer Security (TLS), which provides session confidentiality and the request will not be handled without confidentiality enabled.', ); this.name = 'ConfidentialityRequiredError'; Object.setPrototypeOf(this, ConfidentialityRequiredError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ConstraintViolationError.ts000066400000000000000000000010431522446626000277220ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class ConstraintViolationError extends ResultCodeError { public constructor(message?: string) { super( 19, message ?? 'The attribute value specified in a Add Request, Modify Request or ModifyDNRequest operation violates constraints placed on the attribute. The constraint can be one of size or content (string only, no binary).', ); this.name = 'ConstraintViolationError'; Object.setPrototypeOf(this, ConstraintViolationError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InappropriateAuthError.ts000066400000000000000000000005671522446626000273620ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InappropriateAuthError extends ResultCodeError { public constructor(message?: string) { super(48, message ?? 'The client is attempting to use an authentication method incorrectly.'); this.name = 'InappropriateAuthError'; Object.setPrototypeOf(this, InappropriateAuthError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InappropriateMatchingError.ts000066400000000000000000000006501522446626000302040ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InappropriateMatchingError extends ResultCodeError { public constructor(message?: string) { super(18, message ?? "The matching rule specified in the search filter does not match a rule defined for the attribute's syntax."); this.name = 'InappropriateMatchingError'; Object.setPrototypeOf(this, InappropriateMatchingError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InsufficientAccessError.ts000066400000000000000000000006031522446626000274620ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InsufficientAccessError extends ResultCodeError { public constructor(message?: string) { super(50, message ?? 'The caller does not have sufficient rights to perform the requested operation.'); this.name = 'InsufficientAccessError'; Object.setPrototypeOf(this, InsufficientAccessError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InvalidCredentialsError.ts000066400000000000000000000005411522446626000274570ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InvalidCredentialsError extends ResultCodeError { public constructor(message?: string) { super(49, message ?? 'Invalid credentials during a bind operation.'); this.name = 'InvalidCredentialsError'; Object.setPrototypeOf(this, InvalidCredentialsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InvalidDNSyntaxError.ts000066400000000000000000000005161522446626000267340ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InvalidDNSyntaxError extends ResultCodeError { public constructor(message?: string) { super(34, message ?? 'The syntax of the DN is incorrect.'); this.name = 'InvalidDNSyntaxError'; Object.setPrototypeOf(this, InvalidDNSyntaxError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/InvalidSyntaxError.ts000066400000000000000000000006731522446626000265160ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class InvalidSyntaxError extends ResultCodeError { public constructor(message?: string) { super(21, message ?? 'The attribute value specified in an Add Request, Compare Request, or Modify Request operation is an unrecognized or invalid syntax for the attribute.'); this.name = 'InvalidSyntaxError'; Object.setPrototypeOf(this, InvalidSyntaxError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/IsLeafError.ts000066400000000000000000000005151522446626000250570ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class IsLeafError extends ResultCodeError { public constructor(message?: string) { super(35, message ?? 'The specified operation cannot be performed on a leaf entry.'); this.name = 'IsLeafError'; Object.setPrototypeOf(this, IsLeafError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/LoopDetectError.ts000066400000000000000000000005771522446626000257660ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class LoopDetectError extends ResultCodeError { public constructor(message?: string) { super(54, message ?? 'The client discovered an alias or LDAP Referral loop, and is thus unable to complete this request.'); this.name = 'LoopDetectError'; Object.setPrototypeOf(this, LoopDetectError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/MoreResultsToReturnError.ts000066400000000000000000000004601522446626000277020ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class MoreResultsToReturnError extends ResultCodeError { public constructor(message: string) { super(95, message); this.name = 'MoreResultsToReturnError'; Object.setPrototypeOf(this, MoreResultsToReturnError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NamingViolationError.ts000066400000000000000000000006011522446626000270060ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NamingViolationError extends ResultCodeError { public constructor(message?: string) { super(64, message ?? "The Add Request or Modify DN Request operation violates the schema's structure rules."); this.name = 'NamingViolationError'; Object.setPrototypeOf(this, NamingViolationError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NoObjectClassModsError.ts000066400000000000000000000006021522446626000272250ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NoObjectClassModsError extends ResultCodeError { public constructor(message?: string) { super(69, message ?? 'The modify operation attempted to modify the structure rules of an object class.'); this.name = 'NoObjectClassModsError'; Object.setPrototypeOf(this, NoObjectClassModsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NoResultError.ts000066400000000000000000000004721522446626000254710ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NoResultError extends ResultCodeError { public constructor(message?: string) { super(248, message ?? 'No result message for the request.'); this.name = 'NoResultError'; Object.setPrototypeOf(this, NoResultError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NoSuchAttributeError.ts000066400000000000000000000006231522446626000267770ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NoSuchAttributeError extends ResultCodeError { public constructor(message?: string) { super(16, message ?? 'The attribute specified in the Modify Request or Compare Request operation does not exist in the entry.'); this.name = 'NoSuchAttributeError'; Object.setPrototypeOf(this, NoSuchAttributeError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NoSuchObjectError.ts000066400000000000000000000005051522446626000262410ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NoSuchObjectError extends ResultCodeError { public constructor(message?: string) { super(32, message ?? 'The target object cannot be found.'); this.name = 'NoSuchObjectError'; Object.setPrototypeOf(this, NoSuchObjectError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NotAllowedOnNonLeafError.ts000066400000000000000000000005621522446626000275260ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NotAllowedOnNonLeafError extends ResultCodeError { public constructor(message?: string) { super(66, message ?? 'The requested operation is permitted only on leaf entries.'); this.name = 'NotAllowedOnNonLeafError'; Object.setPrototypeOf(this, NotAllowedOnNonLeafError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/NotAllowedOnRDNError.ts000066400000000000000000000006331522446626000266260ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class NotAllowedOnRDNError extends ResultCodeError { public constructor(message?: string) { super(67, message ?? "The modify operation attempted to remove an attribute value that forms the entry's relative distinguished name."); this.name = 'NotAllowedOnRDNError'; Object.setPrototypeOf(this, NotAllowedOnRDNError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ObjectClassViolationError.ts000066400000000000000000000006411522446626000277750ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class ObjectClassViolationError extends ResultCodeError { public constructor(message?: string) { super(65, message ?? 'The Add Request, Modify Request, or modify DN operation violates the object class rules for the entry.'); this.name = 'ObjectClassViolationError'; Object.setPrototypeOf(this, ObjectClassViolationError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/OperationsError.ts000066400000000000000000000005331522446626000260370ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class OperationsError extends ResultCodeError { public constructor(message?: string) { super(1, message ?? 'Request was out of sequence with another operation in progress.'); this.name = 'OperationsError'; Object.setPrototypeOf(this, OperationsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ProtocolError.ts000066400000000000000000000005401522446626000255130ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class ProtocolError extends ResultCodeError { public constructor(message?: string) { super(2, message ?? 'Client sent data to the server that did not comprise a valid LDAP request.'); this.name = 'ProtocolError'; Object.setPrototypeOf(this, ProtocolError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ResultCodeError.ts000066400000000000000000000004721522446626000257670ustar00rootroot00000000000000export abstract class ResultCodeError extends Error { public code: number; protected constructor(code: number, message: string) { super(`${message} Code: 0x${code.toString(16)}`); this.name = 'ResultCodeError'; this.code = code; Object.setPrototypeOf(this, ResultCodeError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/ResultsTooLargeError.ts000066400000000000000000000005021522446626000270060ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class ResultsTooLargeError extends ResultCodeError { public constructor(message?: string) { super(70, message ?? 'Results are too large.'); this.name = 'ResultsTooLargeError'; Object.setPrototypeOf(this, ResultsTooLargeError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/SaslBindInProgressError.ts000066400000000000000000000011501522446626000274230ustar00rootroot00000000000000import { type BindResponse } from '../../messages/BindResponse.js'; import { ResultCodeError } from './ResultCodeError.js'; export class SaslBindInProgressError extends ResultCodeError { public response: BindResponse; public constructor(response: BindResponse) { super(14, response.errorMessage || 'The server is ready for the next step in the SASL authentication process. The client must send the server the same SASL Mechanism to continue the process.'); this.response = response; this.name = 'SaslBindInProgressError'; Object.setPrototypeOf(this, SaslBindInProgressError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/SizeLimitExceededError.ts000066400000000000000000000007071522446626000272570ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class SizeLimitExceededError extends ResultCodeError { public constructor(message?: string) { super(4, message ?? 'There were more entries matching the criteria contained in a SearchRequest operation than were allowed to be returned by the size limit configuration.'); this.name = 'SizeLimitExceededError'; Object.setPrototypeOf(this, SizeLimitExceededError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/StrongAuthRequiredError.ts000066400000000000000000000005661522446626000275210ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class StrongAuthRequiredError extends ResultCodeError { public constructor(message?: string) { super(8, message ?? 'Client requested an operation that requires strong authentication.'); this.name = 'StrongAuthRequiredError'; Object.setPrototypeOf(this, StrongAuthRequiredError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/TLSNotSupportedError.ts000066400000000000000000000005201522446626000267410ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class TLSNotSupportedError extends ResultCodeError { public constructor(message?: string) { super(112, message ?? 'TLS is not supported on the server.'); this.name = 'TLSNotSupportedError'; Object.setPrototypeOf(this, TLSNotSupportedError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/TimeLimitExceededError.ts000066400000000000000000000010311522446626000272320ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class TimeLimitExceededError extends ResultCodeError { public constructor(message?: string) { super( 3, message ?? 'Processing on the associated request Timeout limit specified by either the client request or the server administration limits has been exceeded and has been terminated because it took too long to complete.', ); this.name = 'TimeLimitExceededError'; Object.setPrototypeOf(this, TimeLimitExceededError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/TypeOrValueExistsError.ts000066400000000000000000000006521522446626000273350ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class TypeOrValueExistsError extends ResultCodeError { public constructor(message?: string) { super(20, message ?? 'The attribute value specified in a Add Request or Modify Request operation already exists as a value for that attribute.'); this.name = 'TypeOrValueExistsError'; Object.setPrototypeOf(this, TypeOrValueExistsError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/UnavailableCriticalExtensionError.ts000066400000000000000000000010251522446626000315040ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class UnavailableCriticalExtensionError extends ResultCodeError { public constructor(message?: string) { super( 12, message ?? 'One or more critical extensions were not available by the LDAP server. Either the server does not support the control or the control is not appropriate for the operation type.', ); this.name = 'UnavailableCriticalExtensionError'; Object.setPrototypeOf(this, UnavailableCriticalExtensionError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/UnavailableError.ts000066400000000000000000000005311522446626000261350ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class UnavailableError extends ResultCodeError { public constructor(message?: string) { super(52, message ?? "The LDAP server cannot process the client's bind request."); this.name = 'UnavailableError'; Object.setPrototypeOf(this, UnavailableError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/UndefinedTypeError.ts000066400000000000000000000006101522446626000264530ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class UndefinedTypeError extends ResultCodeError { public constructor(message?: string) { super(17, message ?? "The attribute specified in the modify or add operation does not exist in the LDAP server's schema."); this.name = 'UndefinedTypeError'; Object.setPrototypeOf(this, UndefinedTypeError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/UnknownStatusCodeError.ts000066400000000000000000000005201522446626000273460ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class UnknownStatusCodeError extends ResultCodeError { public constructor(code: number, message?: string) { super(code, message ?? 'Unknown error.'); this.name = 'UnknownStatusCodeError'; Object.setPrototypeOf(this, UnknownStatusCodeError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/UnwillingToPerformError.ts000066400000000000000000000006071522446626000275240ustar00rootroot00000000000000import { ResultCodeError } from './ResultCodeError.js'; export class UnwillingToPerformError extends ResultCodeError { public constructor(message?: string) { super(53, message ?? 'The LDAP server cannot process the request because of server-defined restrictions.'); this.name = 'UnwillingToPerformError'; Object.setPrototypeOf(this, UnwillingToPerformError.prototype); } } ldapts-ldapts-91d5f4e/src/errors/resultCodeErrors/index.ts000066400000000000000000000033711522446626000240140ustar00rootroot00000000000000export * from './AdminLimitExceededError.js'; export * from './AffectsMultipleDSAsError.js'; export * from './AliasDerefProblemError.js'; export * from './AliasProblemError.js'; export * from './AlreadyExistsError.js'; export * from './AuthMethodNotSupportedError.js'; export * from './BusyError.js'; export * from './ConfidentialityRequiredError.js'; export * from './ConstraintViolationError.js'; export * from './InappropriateAuthError.js'; export * from './InappropriateMatchingError.js'; export * from './InsufficientAccessError.js'; export * from './InvalidCredentialsError.js'; export * from './InvalidDNSyntaxError.js'; export * from './InvalidSyntaxError.js'; export * from './IsLeafError.js'; export * from './LoopDetectError.js'; export * from './MoreResultsToReturnError.js'; export * from './NamingViolationError.js'; export * from './NoObjectClassModsError.js'; export * from './NoSuchAttributeError.js'; export * from './NoSuchObjectError.js'; export * from './NotAllowedOnNonLeafError.js'; export * from './NotAllowedOnRDNError.js'; export * from './NoResultError.js'; export * from './ObjectClassViolationError.js'; export * from './OperationsError.js'; export * from './ProtocolError.js'; export * from './ResultCodeError.js'; export * from './ResultsTooLargeError.js'; export * from './SaslBindInProgressError.js'; export * from './SizeLimitExceededError.js'; export * from './StrongAuthRequiredError.js'; export * from './TimeLimitExceededError.js'; export * from './TLSNotSupportedError.js'; export * from './TypeOrValueExistsError.js'; export * from './UnavailableCriticalExtensionError.js'; export * from './UnavailableError.js'; export * from './UndefinedTypeError.js'; export * from './UnknownStatusCodeError.js'; export * from './UnwillingToPerformError.js'; ldapts-ldapts-91d5f4e/src/filters/000077500000000000000000000000001522446626000171575ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/filters/AndFilter.ts000066400000000000000000000022261522446626000214010ustar00rootroot00000000000000import { type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface AndFilterOptions { filters: Filter[]; } export class AndFilter extends Filter { public type: SearchFilterValues = SearchFilter.and; public filters: Filter[]; public constructor(options: AndFilterOptions) { super(); this.filters = options.filters; } public override writeFilter(writer: BerWriter): void { for (const filter of this.filters) { filter.write(writer); } } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { if (!this.filters.length) { // per RFC4526 return true; } for (const filter of this.filters) { if (!filter.matches(objectToCheck, strictAttributeCase)) { return false; } } return true; } public override toString(): string { let result = '(&'; for (const filter of this.filters) { result += filter.toString(); } result += ')'; return result; } } ldapts-ldapts-91d5f4e/src/filters/ApproximateFilter.ts000066400000000000000000000024051522446626000231670ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface ApproximateFilterOptions { attribute?: string; value?: string; } export class ApproximateFilter extends Filter { public type: SearchFilterValues = SearchFilter.approxMatch; public attribute: string; public value: string; public constructor(options: ApproximateFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; this.value = options.value ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = (reader.readString() ?? '').toLowerCase(); this.value = reader.readString() ?? ''; } public override writeFilter(writer: BerWriter): void { writer.writeString(this.attribute); writer.writeString(this.value); } // eslint-disable-next-line @typescript-eslint/no-unused-vars public override matches(_: Record = {}, __?: boolean): boolean { throw new Error('Approximate match implementation unknown'); } public override toString(): string { return `(${Filter.escape(this.attribute)}~=${Filter.escape(this.value)})`; } } ldapts-ldapts-91d5f4e/src/filters/EqualityFilter.ts000066400000000000000000000037651522446626000225050ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { Ber } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface EqualityFilterOptions { attribute?: string; value?: Buffer | string; } export class EqualityFilter extends Filter { public type: SearchFilterValues = SearchFilter.equalityMatch; public attribute: string; public value: Buffer | string; public constructor(options: EqualityFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; this.value = options.value ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = (reader.readString() ?? '').toLowerCase(); this.value = reader.readString() ?? ''; if (this.attribute === 'objectclass') { this.value = this.value.toLowerCase(); } } public override writeFilter(writer: BerWriter): void { writer.writeString(this.attribute); if (Buffer.isBuffer(this.value)) { writer.writeBuffer(this.value, Ber.OctetString); } else { writer.writeString(this.value); } } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { const objectToCheckValue = this.getObjectValue(objectToCheck, this.attribute, strictAttributeCase); if (typeof objectToCheckValue !== 'undefined') { if (Buffer.isBuffer(this.value) && Buffer.isBuffer(objectToCheckValue)) { return this.value === objectToCheckValue; } const stringValue = Buffer.isBuffer(this.value) ? this.value.toString('utf8') : this.value; if (strictAttributeCase) { return stringValue === objectToCheckValue; } return stringValue.toLowerCase() === objectToCheckValue.toLowerCase(); } return false; } public override toString(): string { return `(${Filter.escape(this.attribute)}=${Filter.escape(this.value)})`; } } ldapts-ldapts-91d5f4e/src/filters/ExtensibleFilter.ts000066400000000000000000000047671522446626000230150ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface ExtensibleFilterOptions { rule?: string; matchType?: string; value?: string; dnAttributes?: boolean; initial?: string; any?: string[]; final?: string; } export class ExtensibleFilter extends Filter { public type: SearchFilterValues = SearchFilter.extensibleMatch; public value: string; public rule: string; public matchType: string; public dnAttributes: boolean; public constructor(options: ExtensibleFilterOptions = {}) { super(); this.matchType = options.matchType ?? ''; this.rule = options.rule ?? ''; this.dnAttributes = options.dnAttributes === true; this.value = options.value ?? ''; } public override parseFilter(reader: BerReader): void { const end = reader.offset + reader.length; while (reader.offset < end) { const tag: number | null = reader.peek(); switch (tag) { case 0x81: this.rule = reader.readString(tag) ?? ''; break; case 0x82: this.matchType = reader.readString(tag) ?? ''; break; case 0x83: this.value = reader.readString(tag) ?? ''; break; case 0x84: this.dnAttributes = reader.readBoolean() ?? false; break; default: { let type = ''; if (tag) { type = `0x${tag.toString(16)}`; } throw new Error(`Invalid ext_match filter type: ${type}`); } } } } public override writeFilter(writer: BerWriter): void { if (this.rule) { writer.writeString(this.rule, 0x81); } if (this.matchType) { writer.writeString(this.matchType, 0x82); } writer.writeString(this.value, 0x83); if (this.dnAttributes) { writer.writeBoolean(this.dnAttributes, 0x84); } } // eslint-disable-next-line @typescript-eslint/no-unused-vars public override matches(_: Record = {}, __?: boolean): boolean { throw new Error('Approximate match implementation unknown'); } public override toString(): string { let result = `(${Filter.escape(this.matchType)}:`; if (this.dnAttributes) { result += 'dn:'; } if (this.rule) { result += `${Filter.escape(this.rule)}:`; } result += `=${Filter.escape(this.value)})`; return result; } } ldapts-ldapts-91d5f4e/src/filters/Filter.ts000066400000000000000000000053631522446626000207630ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; export abstract class Filter { public abstract type: SearchFilterValues; public write(writer: BerWriter): void { writer.startSequence(this.type); this.writeFilter(writer); writer.endSequence(); } public parse(reader: BerReader): void { this.parseFilter(reader); } // eslint-disable-next-line @typescript-eslint/no-unused-vars public matches(_: Record = {}, __?: boolean): boolean { return true; } /** * RFC 2254 Escaping of filter strings * Raw Escaped * (o=Parens (R Us)) (o=Parens \28R Us\29) * (cn=star*) (cn=star\2A) * (filename=C:\MyFile) (filename=C:\5cMyFile) * @param {string|Buffer} input * @returns Escaped string */ public static escape(input: Buffer | string): string { let escapedResult = ''; if (Buffer.isBuffer(input)) { for (const inputChar of input) { if (inputChar < 16) { escapedResult += `\\0${inputChar.toString(16)}`; } else { escapedResult += `\\${inputChar.toString(16)}`; } } } else { for (const inputChar of input) { switch (inputChar) { case '*': escapedResult += '\\2a'; break; case '(': escapedResult += '\\28'; break; case ')': escapedResult += '\\29'; break; case '\\': escapedResult += '\\5c'; break; case '\0': escapedResult += '\\00'; break; default: escapedResult += inputChar; break; } } } return escapedResult; } public abstract toString(): string; // eslint-disable-next-line @typescript-eslint/no-unused-vars protected parseFilter(_: BerReader): void { // Do nothing as the default action } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected writeFilter(_: BerWriter): void { // Do nothing as the default action } protected getObjectValue(objectToCheck: Record, key: string, strictAttributeCase?: boolean): string | undefined { let objectKey: string | undefined; if (typeof objectToCheck[key] !== 'undefined') { objectKey = key; } else if (!strictAttributeCase && key.toLowerCase() === 'objectclass') { for (const objectToCheckKey of Object.keys(objectToCheck)) { if (objectToCheckKey.toLowerCase() === key.toLowerCase()) { objectKey = objectToCheckKey; break; } } } if (objectKey) { return objectToCheck[objectKey]; } return undefined; } } ldapts-ldapts-91d5f4e/src/filters/GreaterThanEqualsFilter.ts000066400000000000000000000030231522446626000242520ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface GreaterThanEqualsFilterOptions { attribute?: string; value?: string; } export class GreaterThanEqualsFilter extends Filter { public type: SearchFilterValues = SearchFilter.greaterOrEqual; public attribute: string; public value: string; public constructor(options: GreaterThanEqualsFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; this.value = options.value ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = reader.readString()?.toLowerCase() ?? ''; this.value = reader.readString() ?? ''; } public override writeFilter(writer: BerWriter): void { writer.writeString(this.attribute); writer.writeString(this.value); } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { const objectToCheckValue = this.getObjectValue(objectToCheck, this.attribute, strictAttributeCase); if (typeof objectToCheckValue !== 'undefined') { if (strictAttributeCase) { return objectToCheckValue >= this.value; } return objectToCheckValue.toLowerCase() >= this.value.toLowerCase(); } return false; } public override toString(): string { return `(${Filter.escape(this.attribute)}>=${Filter.escape(this.value)})`; } } ldapts-ldapts-91d5f4e/src/filters/LessThanEqualsFilter.ts000066400000000000000000000030071522446626000235710ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface LessThanEqualsFilterOptions { attribute?: string; value?: string; } export class LessThanEqualsFilter extends Filter { public type: SearchFilterValues = SearchFilter.lessOrEqual; public attribute: string; public value: string; public constructor(options: LessThanEqualsFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; this.value = options.value ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = reader.readString()?.toLowerCase() ?? ''; this.value = reader.readString() ?? ''; } public override writeFilter(writer: BerWriter): void { writer.writeString(this.attribute); writer.writeString(this.value); } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { const objectToCheckValue = this.getObjectValue(objectToCheck, this.attribute, strictAttributeCase); if (typeof objectToCheckValue !== 'undefined') { if (strictAttributeCase) { return objectToCheckValue <= this.value; } return objectToCheckValue.toLowerCase() <= this.value.toLowerCase(); } return false; } public override toString(): string { return `(${Filter.escape(this.attribute)}<=${Filter.escape(this.value)})`; } } ldapts-ldapts-91d5f4e/src/filters/NotFilter.ts000066400000000000000000000015221522446626000214350ustar00rootroot00000000000000import { type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface NotFilterOptions { filter: Filter; } export class NotFilter extends Filter { public type: SearchFilterValues = SearchFilter.not; public filter: Filter; public constructor(options: NotFilterOptions) { super(); this.filter = options.filter; } public override writeFilter(writer: BerWriter): void { this.filter.write(writer); } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { return !this.filter.matches(objectToCheck, strictAttributeCase); } public override toString(): string { return `(!${this.filter.toString()})`; } } ldapts-ldapts-91d5f4e/src/filters/OrFilter.ts000066400000000000000000000022211522446626000212520ustar00rootroot00000000000000import { type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface OrFilterOptions { filters: Filter[]; } export class OrFilter extends Filter { public type: SearchFilterValues = SearchFilter.or; public filters: Filter[]; public constructor(options: OrFilterOptions) { super(); this.filters = options.filters; } public override writeFilter(writer: BerWriter): void { for (const filter of this.filters) { filter.write(writer); } } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { if (!this.filters.length) { // per RFC4526 return true; } for (const filter of this.filters) { if (filter.matches(objectToCheck, strictAttributeCase)) { return true; } } return false; } public override toString(): string { let result = '(|'; for (const filter of this.filters) { result += filter.toString(); } result += ')'; return result; } } ldapts-ldapts-91d5f4e/src/filters/PresenceFilter.ts000066400000000000000000000024051522446626000224420ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface PresenceFilterOptions { attribute?: string; } export class PresenceFilter extends Filter { public type: SearchFilterValues = SearchFilter.present; public attribute: string; public constructor(options: PresenceFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = reader.buffer.subarray(0, reader.length).toString('utf8').toLowerCase(); reader.offset += reader.length; } public override writeFilter(writer: BerWriter): void { for (let i = 0; i < this.attribute.length; i += 1) { writer.writeByte(this.attribute.charCodeAt(i)); } } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { const objectToCheckValue = this.getObjectValue(objectToCheck, this.attribute, strictAttributeCase); return typeof objectToCheckValue !== 'undefined'; } public override toString(): string { return `(${Filter.escape(this.attribute)}=*)`; } } ldapts-ldapts-91d5f4e/src/filters/SubstringFilter.ts000066400000000000000000000070521522446626000226610ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchFilterValues } from '../SearchFilter.js'; import { SearchFilter } from '../SearchFilter.js'; import { Filter } from './Filter.js'; export interface SubstringFilterOptions { attribute?: string; initial?: string; any?: string[]; final?: string; } export class SubstringFilter extends Filter { public type: SearchFilterValues = SearchFilter.substrings; public attribute: string; public initial: string; public any: string[]; public final: string; public constructor(options: SubstringFilterOptions = {}) { super(); this.attribute = options.attribute ?? ''; this.initial = options.initial ?? ''; this.any = options.any ?? []; this.final = options.final ?? ''; } public override parseFilter(reader: BerReader): void { this.attribute = reader.readString()?.toLowerCase() ?? ''; reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { const tag: number | null = reader.peek(); switch (tag) { case 0x80: this.initial = reader.readString(tag) ?? ''; if (this.attribute === 'objectclass') { this.initial = this.initial.toLowerCase(); } break; case 0x81: { let anyValue: string = reader.readString(tag) ?? ''; if (this.attribute === 'objectclass') { anyValue = anyValue.toLowerCase(); } this.any.push(anyValue); break; } case 0x82: this.final = reader.readString(tag) ?? ''; if (this.attribute === 'objectclass') { this.final = this.final.toLowerCase(); } break; default: { let type = ''; if (tag) { type = `0x${tag.toString(16)}`; } throw new Error(`Invalid substring filter type: ${type}`); } } } } public override writeFilter(writer: BerWriter): void { writer.writeString(this.attribute); writer.startSequence(); if (this.initial) { writer.writeString(this.initial, 0x80); } for (const anyItem of this.any) { writer.writeString(anyItem, 0x81); } if (this.final) { writer.writeString(this.final, 0x82); } writer.endSequence(); } public override matches(objectToCheck: Record = {}, strictAttributeCase?: boolean): boolean { const objectToCheckValue = this.getObjectValue(objectToCheck, this.attribute, strictAttributeCase); if (typeof objectToCheckValue !== 'undefined') { let regexp = ''; if (this.initial) { regexp += `^${SubstringFilter._escapeRegExp(this.initial)}.*`; } for (const anyItem of this.any) { regexp += `${SubstringFilter._escapeRegExp(anyItem)}.*`; } if (this.final) { regexp += `${SubstringFilter._escapeRegExp(this.final)}$`; } // eslint-disable-next-line security/detect-non-literal-regexp const matcher = new RegExp(regexp, strictAttributeCase ? 'gmu' : 'igmu'); return matcher.test(objectToCheckValue); } return false; } public override toString(): string { let result = `(${Filter.escape(this.attribute)}=${Filter.escape(this.initial)}*`; for (const anyItem of this.any) { result += `${Filter.escape(anyItem)}*`; } result += `${Filter.escape(this.final)})`; return result; } private static _escapeRegExp(str: string): string { return str.replace(/[$()*+./?[\\\]^{|}-]/g, '\\$&'); } } ldapts-ldapts-91d5f4e/src/filters/escapeFilter.ts000066400000000000000000000017751522446626000221470ustar00rootroot00000000000000import { Filter } from './Filter.js'; /** * Tagged template literal for building LDAP filter strings. Every interpolated value is escaped * with {@link Filter.escape} (RFC 2254 / RFC 4515), which prevents filter syntax characters in * untrusted input from being misinterpreted as syntax (injection attacks). * @example * const filter = escapeFilter`(&(objectClass=user)(uid=${untrustedInput}))`; * @param {TemplateStringsArray} strings - Literal portions of the template * @param {...Buffer|boolean|number|string} values - Values to escape before interpolating * @returns {string} Filter string with all interpolated values escaped */ export function escapeFilter(strings: TemplateStringsArray, ...values: (Buffer | boolean | number | string)[]): string { let result = strings[0] ?? ''; for (const [index, value] of values.entries()) { result += Filter.escape(typeof value === 'string' || Buffer.isBuffer(value) ? value : String(value)); result += strings[index + 1] ?? ''; } return result; } ldapts-ldapts-91d5f4e/src/filters/index.ts000066400000000000000000000006671522446626000206470ustar00rootroot00000000000000export * from './Filter.js'; export * from './escapeFilter.js'; export * from './AndFilter.js'; export * from './ApproximateFilter.js'; export * from './EqualityFilter.js'; export * from './ExtensibleFilter.js'; export * from './GreaterThanEqualsFilter.js'; export * from './LessThanEqualsFilter.js'; export * from './NotFilter.js'; export * from './OrFilter.js'; export * from './PresenceFilter.js'; export * from './SubstringFilter.js'; ldapts-ldapts-91d5f4e/src/index.ts000066400000000000000000000011251522446626000171650ustar00rootroot00000000000000export * from './ber/index.js'; export * from './controls/index.js'; export * from './dn/DN.js'; export * from './errors/index.js'; export * from './filters/index.js'; export * from './messages/index.js'; export * from './Attribute.js'; export * from './Change.js'; export * from './Client.js'; export * from './ControlParser.js'; export * from './FilterParser.js'; export * from './MessageParser.js'; export * from './MessageResponseStatus.js'; export * from './PostalAddress.js'; export * from './ProtocolOperation.js'; export * from './SearchFilter.js'; export * from './StatusCodeParser.js'; ldapts-ldapts-91d5f4e/src/messages/000077500000000000000000000000001522446626000173165ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/src/messages/AbandonRequest.ts000066400000000000000000000036701522446626000226070ustar00rootroot00000000000000import * as assert from 'node:assert'; import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface AbandonRequestMessageOptions extends MessageOptions { abandonId?: number; } export class AbandonRequest extends Message { public protocolOperation: ProtocolOperationValues; public abandonId: number; public constructor(options: AbandonRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_ABANDON; this.abandonId = options.abandonId ?? 0; } /* eslint-disable no-bitwise */ public override writeMessage(writer: BerWriter): void { // Encode abandon request using different ASN.1 integer logic let i = this.abandonId; let intSize = 4; const mask = 0xff800000; while (((i & mask) === 0 || (i & mask) === mask) && intSize > 1) { intSize -= 1; i <<= 8; } assert.ok(intSize <= 4); while (intSize-- > 0) { writer.writeByte((i & 0xff000000) >> 24); i <<= 8; } } public override parseMessage(reader: BerReader): void { const { length } = reader; if (length) { // Abandon request messages are encoded using different ASN.1 integer logic, forcing custom decoding logic let offset = 1; let value: number; const fb = reader.buffer[offset] ?? 0; value = fb & 0x7f; for (let i = 1; i < length; i += 1) { value <<= 8; offset += 1; const bufferValue = reader.buffer[offset] ?? 0; value |= bufferValue & 0xff; } if ((fb & 0x80) === 0x80) { value = -value; } reader.offset += length; this.abandonId = value; } else { this.abandonId = 0; } } /* eslint-enable no-bitwise */ } ldapts-ldapts-91d5f4e/src/messages/AddRequest.ts000066400000000000000000000025361522446626000217350ustar00rootroot00000000000000import { Attribute } from '../Attribute.js'; import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface AddMessageOptions extends MessageOptions { dn: string; attributes?: Attribute[]; } export class AddRequest extends Message { public protocolOperation: ProtocolOperationValues; public dn: string; public attributes: Attribute[]; public constructor(options: AddMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_ADD; this.dn = options.dn; this.attributes = options.attributes ?? []; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.dn); writer.startSequence(); for (const attribute of this.attributes) { attribute.write(writer); } writer.endSequence(); } public override parseMessage(reader: BerReader): void { this.dn = reader.readString() ?? ''; reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { const attribute = new Attribute(); attribute.parse(reader); this.attributes.push(attribute); } } } ldapts-ldapts-91d5f4e/src/messages/AddResponse.ts000066400000000000000000000007731522446626000221040ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export class AddResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_ADD; } } ldapts-ldapts-91d5f4e/src/messages/BindRequest.ts000066400000000000000000000040141522446626000221120ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { Ber } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export const SASL_MECHANISMS = ['EXTERNAL', 'PLAIN', 'DIGEST-MD5', 'SCRAM-SHA-1'] as const; export type SaslMechanism = (typeof SASL_MECHANISMS)[number]; export interface BindRequestMessageOptions extends MessageOptions { dn?: string; password?: string; mechanism?: string; } export class BindRequest extends Message { public protocolOperation: ProtocolOperationValues; public dn: string; public password: string; public mechanism: string | undefined; public constructor(options: BindRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_BIND; this.dn = options.dn ?? ''; this.password = options.password ?? ''; this.mechanism = options.mechanism; } public override writeMessage(writer: BerWriter): void { writer.writeInt(this.version); writer.writeString(this.dn); if (this.mechanism) { // SASL authentication writer.startSequence(ProtocolOperation.LDAP_REQ_BIND_SASL); writer.writeString(this.mechanism); writer.writeString(this.password); writer.endSequence(); } else { // Simple authentication writer.writeString(this.password, Ber.Context); // 128 } } public override parseMessage(reader: BerReader): void { this.version = reader.readInt() ?? ProtocolOperation.LDAP_VERSION_3; this.dn = reader.readString() ?? ''; const contextCheck: number | null = reader.peek(); if (contextCheck !== Ber.Context) { let type = ''; if (contextCheck) { type = `0x${contextCheck.toString(16)}`; } throw new Error(`Authentication type not supported: ${type}`); } this.password = reader.readString(Ber.Context) ?? ''; } } ldapts-ldapts-91d5f4e/src/messages/BindResponse.ts000066400000000000000000000016371522446626000222700ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export class BindResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public data: string[] = []; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_BIND; } public override parseMessage(reader: BerReader): void { super.parseMessage(reader); while (reader.remain > 0) { const type = reader.peek(); if (type === ProtocolOperation.LDAP_CONTROLS) { break; } this.data.push(reader.readString(typeof type === 'number' ? type : undefined) ?? ''); } } } ldapts-ldapts-91d5f4e/src/messages/CompareRequest.ts000066400000000000000000000024651522446626000226340ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface CompareRequestMessageOptions extends MessageOptions { dn?: string; attribute?: string; value?: string; } export class CompareRequest extends Message { public protocolOperation: ProtocolOperationValues; public dn: string; public attribute: string; public value: string; public constructor(options: CompareRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_COMPARE; this.attribute = options.attribute ?? ''; this.value = options.value ?? ''; this.dn = options.dn ?? ''; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.dn); writer.startSequence(); writer.writeString(this.attribute); writer.writeString(this.value); writer.endSequence(); } public override parseMessage(reader: BerReader): void { this.dn = reader.readString() ?? ''; reader.readSequence(); this.attribute = (reader.readString() ?? '').toLowerCase(); this.value = reader.readString() ?? ''; } } ldapts-ldapts-91d5f4e/src/messages/CompareResponse.ts000066400000000000000000000020621522446626000227730ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export enum CompareResult { /** * Indicates that the target entry exists and contains the specified attribute with the indicated value */ compareTrue = 0x06, /** * Indicates that the target entry exists and contains the specified attribute, but that the attribute does not have the indicated value */ compareFalse = 0x05, /** * Indicates that the target entry exists but does not contain the specified attribute */ noSuchAttribute = 0x16, /** * Indicates that the target entry does not exist */ noSuchObject = 0x32, } export class CompareResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_COMPARE; } } ldapts-ldapts-91d5f4e/src/messages/DeleteRequest.ts000066400000000000000000000020361522446626000224420ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface DeleteRequestMessageOptions extends MessageOptions { dn?: string; } export class DeleteRequest extends Message { public protocolOperation: ProtocolOperationValues; public dn: string; public constructor(options: DeleteRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_DELETE; this.dn = options.dn ?? ''; } public override writeMessage(writer: BerWriter): void { const buffer = Buffer.from(this.dn); for (const byte of buffer) { writer.writeByte(byte); } } public override parseMessage(reader: BerReader): void { const { length } = reader; this.dn = reader.buffer.subarray(0, length).toString('utf8'); reader.offset += reader.length; } } ldapts-ldapts-91d5f4e/src/messages/DeleteResponse.ts000066400000000000000000000010011522446626000225770ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export class DeleteResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_DELETE; } } ldapts-ldapts-91d5f4e/src/messages/ExtendedRequest.ts000066400000000000000000000025331522446626000230020ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface ExtendedRequestMessageOptions extends MessageOptions { oid?: string; value?: Buffer | string; } export class ExtendedRequest extends Message { public protocolOperation: ProtocolOperationValues; public oid: string; public value: Buffer | string; public constructor(options: ExtendedRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_EXTENSION; this.oid = options.oid ?? ''; this.value = options.value ?? ''; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.oid, 0x80); if (Buffer.isBuffer(this.value)) { writer.writeBuffer(this.value, 0x81); } else if (this.value) { writer.writeString(this.value, 0x81); } } public override parseMessage(reader: BerReader): void { this.oid = reader.readString(0x80) ?? ''; if (reader.peek() === 0x81) { try { this.value = reader.readString(0x81) ?? ''; } catch { this.value = reader.readString(0x81, true) ?? Buffer.alloc(0); } } } } ldapts-ldapts-91d5f4e/src/messages/ExtendedResponse.ts000066400000000000000000000024201522446626000231430ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export interface ExtendedResponseOptions extends MessageResponseOptions { oid?: string; value?: string; } export const ExtendedResponseProtocolOperations = { oid: 0x8a, value: 0x8b, }; export class ExtendedResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public oid?: string; public value?: string; public constructor(options: ExtendedResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_EXTENSION; this.oid = options.oid; this.value = options.value; } public override parseMessage(reader: BerReader): void { super.parseMessage(reader); if (reader.peek() === ExtendedResponseProtocolOperations.oid) { this.oid = reader.readString(ExtendedResponseProtocolOperations.oid) ?? ''; } if (reader.peek() === ExtendedResponseProtocolOperations.value) { this.value = reader.readString(ExtendedResponseProtocolOperations.value) ?? undefined; } } } ldapts-ldapts-91d5f4e/src/messages/Message.ts000066400000000000000000000042741522446626000212610ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { BerWriter } from '../ber/index.js'; import { ControlParser } from '../ControlParser.js'; import { type Control } from '../controls/Control.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; export interface MessageOptions { messageId: number; controls?: Control[]; } export abstract class Message { public version: number = ProtocolOperation.LDAP_VERSION_3; public messageId = 0; public abstract protocolOperation: ProtocolOperationValues; public controls?: Control[]; protected constructor(options: MessageOptions) { this.messageId = options.messageId; this.controls = options.controls; } public write(): Buffer { const writer = new BerWriter(); writer.startSequence(); writer.writeInt(this.messageId); writer.startSequence(this.protocolOperation); this.writeMessage(writer); writer.endSequence(); if (this.controls?.length) { writer.startSequence(ProtocolOperation.LDAP_CONTROLS); for (const control of this.controls) { control.write(writer); } writer.endSequence(); } writer.endSequence(); return writer.buffer; } public parse(reader: BerReader, requestControls: Control[]): void { this.controls = []; this.parseMessage(reader); if (reader.peek() === ProtocolOperation.LDAP_CONTROLS) { reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { const control = ControlParser.parse(reader, requestControls); if (control) { this.controls.push(control); } } } } public toString(): string { return JSON.stringify( { messageId: this.messageId, messageType: this.constructor.name, }, null, 2, ); } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected parseMessage(_: BerReader): void { // Do nothing as the default action } // eslint-disable-next-line @typescript-eslint/no-unused-vars protected writeMessage(_: BerWriter): void { // Do nothing as the default action } } ldapts-ldapts-91d5f4e/src/messages/MessageResponse.ts000066400000000000000000000015461522446626000227770ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface MessageResponseOptions extends MessageOptions { status?: number; matchedDN?: string; errorMessage?: string; } export abstract class MessageResponse extends Message { public status: number; public matchedDN: string; public errorMessage: string; protected constructor(options: MessageResponseOptions) { super(options); this.status = options.status ?? 0; // LDAP Success this.matchedDN = options.matchedDN ?? ''; this.errorMessage = options.errorMessage ?? ''; } public override parseMessage(reader: BerReader): void { this.status = reader.readEnumeration() ?? 0; this.matchedDN = reader.readString() ?? ''; this.errorMessage = reader.readString() ?? ''; } } ldapts-ldapts-91d5f4e/src/messages/ModifyDNRequest.ts000066400000000000000000000030261522446626000227110ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface ModifyDNRequestMessageOptions extends MessageOptions { deleteOldRdn?: boolean; dn?: string; newRdn?: string; newSuperior?: string; } export class ModifyDNRequest extends Message { public protocolOperation: ProtocolOperationValues; public deleteOldRdn: boolean; public dn: string; public newRdn: string; public newSuperior: string; public constructor(options: ModifyDNRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_MODRDN; this.deleteOldRdn = options.deleteOldRdn !== false; this.dn = options.dn ?? ''; this.newRdn = options.newRdn ?? ''; this.newSuperior = options.newSuperior ?? ''; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.dn); writer.writeString(this.newRdn); writer.writeBoolean(this.deleteOldRdn); if (this.newSuperior) { writer.writeString(this.newSuperior, 0x80); } } public override parseMessage(reader: BerReader): void { this.dn = reader.readString() ?? ''; this.newRdn = reader.readString() ?? ''; this.deleteOldRdn = reader.readBoolean() ?? false; if (reader.peek() === 0x80) { this.newSuperior = reader.readString(0x80) ?? ''; } } } ldapts-ldapts-91d5f4e/src/messages/ModifyDNResponse.ts000066400000000000000000000010031522446626000230500ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export class ModifyDNResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_MODRDN; } } ldapts-ldapts-91d5f4e/src/messages/ModifyRequest.ts000066400000000000000000000025171522446626000224730ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { Change } from '../Change.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface ModifyRequestMessageOptions extends MessageOptions { dn?: string; changes?: Change[]; } export class ModifyRequest extends Message { public protocolOperation: ProtocolOperationValues; public dn: string; public changes: Change[]; public constructor(options: ModifyRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_MODIFY; this.dn = options.dn ?? ''; this.changes = options.changes ?? []; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.dn); writer.startSequence(); for (const change of this.changes) { change.write(writer); } writer.endSequence(); } public override parseMessage(reader: BerReader): void { this.dn = reader.readString() ?? ''; reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { const change = new Change(); change.parse(reader); this.changes.push(change); } } } ldapts-ldapts-91d5f4e/src/messages/ModifyResponse.ts000066400000000000000000000010011522446626000226240ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export class ModifyResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_MODIFY; } } ldapts-ldapts-91d5f4e/src/messages/SearchEntry.ts000066400000000000000000000044551522446626000221250ustar00rootroot00000000000000import { Attribute } from '../Attribute.js'; import { type BerReader } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export interface SearchEntryOptions extends MessageResponseOptions { name?: string; attributes?: Attribute[]; } export interface Entry { dn: string; [index: string]: Buffer | Buffer[] | string[] | string; } export class SearchEntry extends MessageResponse { public protocolOperation: ProtocolOperationValues; public name: string; public attributes: Attribute[]; public constructor(options: SearchEntryOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_SEARCH_ENTRY; this.name = options.name ?? ''; this.attributes = options.attributes ?? []; } public override parseMessage(reader: BerReader): void { this.name = reader.readString() ?? ''; reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { const attribute = new Attribute(); attribute.parse(reader); this.attributes.push(attribute); } } public toObject(requestAttributes: string[], explicitBufferAttributes: string[]): Entry { const result: Entry = { dn: this.name, }; const resultLCAttributes = new Set(); for (const attribute of this.attributes) { resultLCAttributes.add(attribute.type.toLocaleLowerCase()); let { values } = attribute; if (explicitBufferAttributes.includes(attribute.type) || values.some((value) => Buffer.isBuffer(value))) { values = attribute.parsedBuffers; } if (values.length) { if (values.length === 1) { result[attribute.type] = values[0] ?? []; } else { result[attribute.type] = values; } } else { result[attribute.type] = []; } } // Fill in any missing attributes that were requested for (const attribute of requestAttributes) { if (typeof result[attribute] === 'undefined' && !resultLCAttributes.has(attribute.toLocaleLowerCase())) { result[attribute] = []; } } return result; } } ldapts-ldapts-91d5f4e/src/messages/SearchReference.ts000066400000000000000000000016631522446626000227200ustar00rootroot00000000000000import { type BerReader } from '../ber/index.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; export interface SearchReferenceOptions extends MessageResponseOptions { uris?: string[]; } export class SearchReference extends MessageResponse { public protocolOperation: ProtocolOperationValues; public uris: string[]; public constructor(options: SearchReferenceOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_SEARCH_REF; this.uris = options.uris ?? []; } public override parseMessage(reader: BerReader): void { const end = reader.offset + reader.length; while (reader.offset < end) { const url = reader.readString() ?? ''; this.uris.push(url); } } } ldapts-ldapts-91d5f4e/src/messages/SearchRequest.ts000066400000000000000000000105211522446626000224430ustar00rootroot00000000000000import { type BerReader, type BerWriter } from '../ber/index.js'; import { type SearchOptions } from '../Client.js'; import { FilterParser } from '../FilterParser.js'; import { type Filter } from '../filters/Filter.js'; import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export interface SearchRequestMessageOptions extends MessageOptions, SearchOptions { baseDN?: string; filter: Filter; } export class SearchRequest extends Message { public protocolOperation: ProtocolOperationValues; public baseDN: string; public scope: 'base' | 'children' | 'one' | 'sub' | 'subordinates'; public derefAliases: 'always' | 'find' | 'never' | 'search'; public sizeLimit: number; public timeLimit: number; public returnAttributeValues: boolean; public filter: Filter; public attributes: string[]; public explicitBufferAttributes: string[]; public constructor(options: SearchRequestMessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_SEARCH; this.baseDN = options.baseDN ?? ''; this.scope = options.scope ?? 'sub'; this.derefAliases = options.derefAliases ?? 'never'; this.sizeLimit = options.sizeLimit ?? 0; this.timeLimit = options.timeLimit ?? 10; this.returnAttributeValues = options.returnAttributeValues !== false; this.filter = options.filter; this.attributes = options.attributes ?? []; this.explicitBufferAttributes = options.explicitBufferAttributes ?? []; } public override writeMessage(writer: BerWriter): void { writer.writeString(this.baseDN); switch (this.scope) { case 'base': writer.writeEnumeration(0); break; case 'one': writer.writeEnumeration(1); break; case 'sub': writer.writeEnumeration(2); break; case 'children': case 'subordinates': writer.writeEnumeration(3); break; default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions throw new Error(`Invalid search scope: ${this.scope}`); } switch (this.derefAliases) { case 'never': writer.writeEnumeration(0); break; case 'search': writer.writeEnumeration(1); break; case 'find': writer.writeEnumeration(2); break; case 'always': writer.writeEnumeration(3); break; default: // eslint-disable-next-line @typescript-eslint/restrict-template-expressions throw new Error(`Invalid deref alias: ${this.derefAliases}`); } writer.writeInt(this.sizeLimit); writer.writeInt(this.timeLimit); writer.writeBoolean(!this.returnAttributeValues); this.filter.write(writer); writer.startSequence(); for (const attribute of this.attributes) { writer.writeString(attribute); } writer.endSequence(); } public override parseMessage(reader: BerReader): void { this.baseDN = reader.readString() ?? ''; const scope = reader.readEnumeration(); switch (scope) { case 0: this.scope = 'base'; break; case 1: this.scope = 'one'; break; case 2: this.scope = 'sub'; break; case 3: this.scope = 'children'; break; default: throw new Error(`Invalid search scope: ${scope ?? ''}`); } const derefAliases = reader.readEnumeration(); switch (scope) { case 0: this.derefAliases = 'never'; break; case 1: this.derefAliases = 'search'; break; case 2: this.derefAliases = 'find'; break; case 3: this.derefAliases = 'always'; break; default: throw new Error(`Invalid deref alias: ${derefAliases ?? ''}`); } this.sizeLimit = reader.readInt() ?? 0; this.timeLimit = reader.readInt() ?? 0; this.returnAttributeValues = !(reader.readBoolean() ?? false); this.filter = FilterParser.parse(reader); if (reader.peek() === 0x30) { reader.readSequence(); const end = reader.offset + reader.length; while (reader.offset < end) { this.attributes.push((reader.readString() ?? '').toLowerCase()); } } } } ldapts-ldapts-91d5f4e/src/messages/SearchResponse.ts000066400000000000000000000017171522446626000226200ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageResponseOptions } from './MessageResponse.js'; import { MessageResponse } from './MessageResponse.js'; import { type SearchEntry } from './SearchEntry.js'; import { type SearchReference } from './SearchReference.js'; export interface SearchResponseOptions extends MessageResponseOptions { searchEntries?: SearchEntry[]; searchReferences?: SearchReference[]; } export class SearchResponse extends MessageResponse { public protocolOperation: ProtocolOperationValues; public searchEntries: SearchEntry[]; public searchReferences: SearchReference[]; public constructor(options: SearchResponseOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_RES_SEARCH; this.searchEntries = options.searchEntries ?? []; this.searchReferences = options.searchReferences ?? []; } } ldapts-ldapts-91d5f4e/src/messages/UnbindRequest.ts000066400000000000000000000007201522446626000224550ustar00rootroot00000000000000import { type ProtocolOperationValues } from '../ProtocolOperation.js'; import { ProtocolOperation } from '../ProtocolOperation.js'; import { type MessageOptions } from './Message.js'; import { Message } from './Message.js'; export class UnbindRequest extends Message { public protocolOperation: ProtocolOperationValues; public constructor(options: MessageOptions) { super(options); this.protocolOperation = ProtocolOperation.LDAP_REQ_UNBIND; } } ldapts-ldapts-91d5f4e/src/messages/index.ts000066400000000000000000000013311522446626000207730ustar00rootroot00000000000000export * from './AbandonRequest.js'; export * from './AddRequest.js'; export * from './AddResponse.js'; export * from './BindRequest.js'; export * from './BindResponse.js'; export * from './CompareRequest.js'; export * from './CompareResponse.js'; export * from './DeleteRequest.js'; export * from './DeleteResponse.js'; export * from './ExtendedRequest.js'; export * from './ExtendedResponse.js'; export * from './ModifyDNRequest.js'; export * from './ModifyDNResponse.js'; export * from './ModifyRequest.js'; export * from './ModifyResponse.js'; export * from './SearchEntry.js'; export * from './SearchReference.js'; export * from './SearchRequest.js'; export * from './SearchResponse.js'; export * from './UnbindRequest.js'; ldapts-ldapts-91d5f4e/tests/000077500000000000000000000000001522446626000160625ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/Client.test.ts000066400000000000000000001245251522446626000206370ustar00rootroot00000000000000import { promises as fs } from 'node:fs'; import * as net from 'node:net'; import * as path from 'node:path'; import * as tls from 'node:tls'; import { fileURLToPath } from 'node:url'; import { afterAll, beforeAll, describe, expect, it, vi } from 'vite-plus/test'; import { type BerReader, type BerWriter } from '../src/ber/index.js'; import { type AddRequest, type ModifyDNRequest } from '../src/index.js'; import { AddResponse, AndFilter, Attribute, Change, Client, Control, EqualityFilter, InvalidCredentialsError, InvalidDNSyntaxError, ModifyDNResponse, NoSuchObjectError, PagedResultsControl, UndefinedTypeError, } from '../src/index.js'; process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; const LDAP_DOMAIN = 'ldap.local'; const LDAP_URI = 'ldap://localhost:389'; const SECURE_LDAP_URI = 'ldaps://localhost:636'; const BASE_DN = 'dc=ldap,dc=local'; const BIND_DN = `cn=admin,${BASE_DN}`; const BIND_PW = '1234'; describe('Client', () => { describe('#constructor()', () => { it('should throw error if url protocol is not ldap:// or ldaps://', () => { const url = 'https://127.0.0.1'; expect((): void => { new Client({ url, }); }).toThrow(`${url} is an invalid LDAP URL (protocol)`); }); it('should not throw error if url protocol is ldap://', () => { const url = 'ldap://127.0.0.1'; expect((): void => { new Client({ url, }); }).not.toThrow(); }); it('should not throw error if url protocol is ldaps://', () => { const url = 'ldaps://127.0.0.1'; expect((): void => { new Client({ url, }); }).not.toThrow(); }); it('should not enable secure mode with empty tlsOptions object', () => { const client = new Client({ url: 'ldap://127.0.0.1', tlsOptions: {}, }); // @ts-expect-error - private field expect(client.secure).toBe(false); }); it('should not enable secure mode with tlsOptions containing only undefined values', () => { const client = new Client({ url: 'ldap://127.0.0.1', tlsOptions: { rejectUnauthorized: undefined, ca: undefined, }, }); // @ts-expect-error - private field expect(client.secure).toBe(false); }); it('should enable secure mode with tlsOptions containing defined values', () => { const client = new Client({ url: 'ldap://127.0.0.1', tlsOptions: { rejectUnauthorized: false, }, }); // @ts-expect-error - private field expect(client.secure).toBe(true); }); it('should enable secure mode with ldaps:// even with empty tlsOptions', () => { const client = new Client({ url: 'ldaps://127.0.0.1', tlsOptions: {}, }); // @ts-expect-error - private field expect(client.secure).toBe(true); }); // URL parsing tests to ensure native URL class handles all cases correctly describe('URL parsing', () => { it('should parse ldap URL with explicit port', () => { const client = new Client({ url: 'ldap://localhost:389', }); // @ts-expect-error - private field expect(client.host).toBe('localhost'); // @ts-expect-error - private field expect(client.port).toBe(389); // @ts-expect-error - private field expect(client.secure).toBe(false); }); it('should parse ldaps URL with explicit port', () => { const client = new Client({ url: 'ldaps://localhost:636', }); // @ts-expect-error - private field expect(client.host).toBe('localhost'); // @ts-expect-error - private field expect(client.port).toBe(636); // @ts-expect-error - private field expect(client.secure).toBe(true); }); it('should use default port 389 for ldap URL without port', () => { const client = new Client({ url: 'ldap://localhost', }); // @ts-expect-error - private field expect(client.port).toBe(389); }); it('should use default port 636 for ldaps URL without port', () => { const client = new Client({ url: 'ldaps://localhost', }); // @ts-expect-error - private field expect(client.port).toBe(636); }); it('should parse IPv4 address', () => { const client = new Client({ url: 'ldap://192.168.1.1:389', }); // @ts-expect-error - private field expect(client.host).toBe('192.168.1.1'); // @ts-expect-error - private field expect(client.port).toBe(389); }); it('should parse IPv6 address with brackets', () => { const client = new Client({ url: 'ldap://[::1]:389', }); // @ts-expect-error - private field // IPv6 can be represented as '::1' or '0:0:0:0:0:0:0:1' - both are valid expect(client.host).toMatch(/^(:{2}1|(?:0:){7}1)$/); // @ts-expect-error - private field expect(client.port).toBe(389); }); it('should parse full IPv6 address', () => { const client = new Client({ url: 'ldap://[2001:db8:85a3::8a2e:370:7334]:389', }); // @ts-expect-error - private field // The host should be a valid representation of the IPv6 address (compressed or expanded) expect(typeof client.host).toBe('string'); // @ts-expect-error - private field expect(client.host.length).toBeGreaterThan(0); // @ts-expect-error - private field expect(client.port).toBe(389); }); it('should parse IPv6 address without port', () => { const client = new Client({ url: 'ldap://[::1]', }); // @ts-expect-error - private field expect(client.host).toMatch(/^(:{2}1|(?:0:){7}1)$/); // @ts-expect-error - private field expect(client.port).toBe(389); }); it('should parse hostname with subdomain', () => { const client = new Client({ url: 'ldap://ldap.example.com:389', }); // @ts-expect-error - private field expect(client.host).toBe('ldap.example.com'); }); it('should use custom port when specified', () => { const client = new Client({ url: 'ldap://localhost:1389', }); // @ts-expect-error - private field expect(client.port).toBe(1389); }); it('should throw error for http:// protocol', () => { expect((): void => { new Client({ url: 'http://localhost:389', }); }).toThrow('http://localhost:389 is an invalid LDAP URL (protocol)'); }); it('should throw error for https:// protocol', () => { expect((): void => { new Client({ url: 'https://localhost:389', }); }).toThrow('https://localhost:389 is an invalid LDAP URL (protocol)'); }); it('should throw error for ftp:// protocol', () => { expect((): void => { new Client({ url: 'ftp://localhost:389', }); }).toThrow('ftp://localhost:389 is an invalid LDAP URL (protocol)'); }); it('should throw error for malformed URL', () => { expect((): void => { new Client({ url: 'not-a-valid-url', }); }).toThrow('not-a-valid-url is an invalid LDAP URL (protocol)'); }); it('should throw error for empty URL', () => { expect((): void => { new Client({ url: '', }); }).toThrow(' is an invalid LDAP URL (protocol)'); }); it('should handle URL with empty host', () => { // ldap:/// is a valid URL format - behavior may vary // The client should not throw and should have a valid host const client = new Client({ url: 'ldap:///', }); // @ts-expect-error - private field expect(typeof client.host).toBe('string'); // @ts-expect-error - private field expect(client.port).toBe(389); }); }); }); describe('#isConnected', () => { it('should not be connected if a method has not been called', () => { const client = new Client({ url: LDAP_URI, }); expect(client.isConnected).toBe(false); }); it('should not be connected after unbind has been called', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); expect(client.isConnected).toBe(true); await client.unbind(); expect(client.isConnected).toBe(false); }); it('should be connected if a method has been called', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); expect(client.isConnected).toBe(true); try { await client.unbind(); } catch { // This can fail since it's not the part being tested } }); it('should allow bind/unbind to be called multiple times without error', async () => { const client = new Client({ url: LDAP_URI, }); expect(client.isConnected).toBe(false); await client.bind(BIND_DN, BIND_PW); expect(client.isConnected).toBe(true); await client.unbind(); expect(client.isConnected).toBe(false); await client.bind(BIND_DN, BIND_PW); expect(client.isConnected).toBe(true); await client.unbind(); expect(client.isConnected).toBe(false); }); }); describe('#bind()', () => { it('should succeed on basic bind', async () => { const client = new Client({ url: LDAP_URI, }); await expect(client.bind(BIND_DN, BIND_PW)).resolves.toBeUndefined(); try { await client.unbind(); } catch { // This can fail since it's not the part being tested } }); it('should succeed with ldaps://', async () => { await using client = new Client({ url: SECURE_LDAP_URI, }); // @ts-expect-error - private field expect(client.secure).toBe(true); await client.bind(BIND_DN, BIND_PW); }); it('should throw for invalid credentials', async () => { const client = new Client({ url: LDAP_URI, }); await expect(client.bind(BIND_DN, 'AlsoNotAHotdog')).rejects.toBeInstanceOf(InvalidCredentialsError); await client.unbind(); }); it('should bind using EXTERNAL sasl mechanism', async () => { const client = new Client({ url: LDAP_URI, }); const testsDirectory = fileURLToPath(new URL('.', import.meta.url)); const [ca, cert, key] = await Promise.all([ // eslint-disable-next-line security/detect-non-literal-fs-filename fs.readFile(path.join(testsDirectory, './data/certs/ca.pem')), // eslint-disable-next-line security/detect-non-literal-fs-filename fs.readFile(path.join(testsDirectory, './data/certs/client.pem')), // eslint-disable-next-line security/detect-non-literal-fs-filename fs.readFile(path.join(testsDirectory, './data/certs/client-key.pem')), ]); await client.startTLS({ ca, cert, key, }); await expect(client.bind('EXTERNAL')).resolves.toBeUndefined(); try { await client.unbind(); } catch { // This can fail since it's not the part being tested } }); it('should bind with a custom control', async () => { // Get list of supported controls and extensions: // ldapsearch -H ldaps://localhost:636 -b "" -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm -s base supportedFeatures supportedControl supportedExtension let hasParsed = false; let hasWritten = false; class PasswordPolicyControl extends Control { public constructor() { super('1.3.6.1.4.1.42.2.27.8.5.1'); } public override parseControl(reader: BerReader): void { // Should be called as part of the response from the server hasParsed = true; super.parseControl(reader); } public override writeControl(writer: BerWriter): void { // Should be called as part of the request to the server hasWritten = true; super.writeControl(writer); } } const testControl = new PasswordPolicyControl(); const client = new Client({ url: LDAP_URI, }); await client.bind(`uid=user2,${BASE_DN}`, BIND_PW); try { await client.modify( `uid=user2,${BASE_DN}`, new Change({ operation: 'replace', modification: new Attribute({ type: 'userPassword', values: ['1234'], }), }), testControl, ); expect.unreachable('Exception expected'); } catch { // surely will happen } expect(hasWritten).toBe(true); expect(hasParsed).toBe(true); }); }); describe('#startTLS()', () => { it('should upgrade an existing clear-text connection to be secure', async () => { const client = new Client({ url: LDAP_URI, }); await expect(client.startTLS()).resolves.toBeUndefined(); }); it('should use secure connection for subsequent operations', async () => { const client = new Client({ url: LDAP_URI, }); await client.startTLS(); await client.bind(BIND_DN, BIND_PW); await expect(client.unbind()).resolves.toBeUndefined(); }); }); describe('custom connection factories', () => { it('should use createConnection for ldap:// connections', async () => { const createConnection = vi.fn(net.connect); const client = new Client({ url: LDAP_URI, createConnection: createConnection as unknown as typeof net.connect, }); await client.bind(BIND_DN, BIND_PW); expect(createConnection).toHaveBeenCalledTimes(1); expect(createConnection).toHaveBeenCalledWith(389, 'localhost'); await client.unbind(); }); it('should support createConnection returning an already-established socket', async () => { const socket = net.connect(389, 'localhost'); await new Promise((resolve, reject) => { socket.once('connect', () => { resolve(); }); socket.once('error', reject); }); const client = new Client({ url: LDAP_URI, createConnection: () => socket, }); await client.bind(BIND_DN, BIND_PW); await expect(client.unbind()).resolves.toBeUndefined(); }); it('should use createSecureConnection for ldaps:// connections', async () => { const createSecureConnection = vi.fn(tls.connect); const client = new Client({ url: SECURE_LDAP_URI, createSecureConnection: createSecureConnection as unknown as typeof tls.connect, }); await client.bind(BIND_DN, BIND_PW); expect(createSecureConnection).toHaveBeenCalledTimes(1); expect(createSecureConnection).toHaveBeenCalledWith(636, 'localhost', undefined); await client.unbind(); }); it('should use createSecureConnection for startTLS() upgrades', async () => { const createSecureConnection = vi.fn(tls.connect); const client = new Client({ url: LDAP_URI, createSecureConnection: createSecureConnection as unknown as typeof tls.connect, }); await client.startTLS(); expect(createSecureConnection).toHaveBeenCalledTimes(1); await client.bind(BIND_DN, BIND_PW); await expect(client.unbind()).resolves.toBeUndefined(); }); }); describe('#autoRebind', () => { const WHO_AM_I_OID = '1.3.6.1.4.1.4203.1.11.3'; it('should expose bind state via isBound', async () => { const client = new Client({ url: LDAP_URI, }); expect(client.isBound).toBe(false); await client.bind(BIND_DN, BIND_PW); expect(client.isBound).toBe(true); await client.unbind(); expect(client.isBound).toBe(false); }); it('should rebind automatically when the connection is re-established', async () => { const client = new Client({ url: LDAP_URI, autoRebind: true, }); await client.bind(BIND_DN, BIND_PW); // Simulate the server unexpectedly dropping the connection // @ts-expect-error - private field client.socket.destroy(); await vi.waitFor(() => { expect(client.isConnected).toBe(false); }); const result = await client.exop(WHO_AM_I_OID); expect(client.isBound).toBe(true); expect(result.value).toContain('cn=admin'); await client.unbind(); }); it('should reconnect anonymously when autoRebind is not enabled', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); // @ts-expect-error - private field client.socket.destroy(); await vi.waitFor(() => { expect(client.isConnected).toBe(false); }); const result = await client.exop(WHO_AM_I_OID); expect(client.isBound).toBe(false); expect(result.value ?? '').not.toContain('cn=admin'); await client.unbind(); }); it('should not rebind after an explicit unbind', async () => { const client = new Client({ url: LDAP_URI, autoRebind: true, }); await client.bind(BIND_DN, BIND_PW); await client.unbind(); const result = await client.exop(WHO_AM_I_OID); expect(client.isBound).toBe(false); expect(result.value ?? '').not.toContain('cn=admin'); await client.unbind(); }); }); describe('#unbind()', () => { it('should succeed on basic unbind after successful bind', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); await expect(client.unbind()).resolves.toBeUndefined(); }); it('should succeed if client.bind() was not called previously', async () => { const client = new Client({ url: LDAP_URI, }); await expect(client.unbind()).resolves.toBeUndefined(); }); it('should allow unbind to be called multiple times without error', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); await Promise.all([client.unbind(), client.unbind()]); await expect(client.unbind()).resolves.toBeUndefined(); }); it('should destroy socket after unbind', async () => { const client = new Client({ connectTimeout: 5000, url: 'ldaps://localhost:389', }); try { // @ts-expect-error - is private await client._connect(); await client.bind(BIND_DN, BIND_PW); await client.unbind(); } catch { // ignore } finally { // @ts-expect-error - is private expect(client.connectTimer).toBeUndefined(); // @ts-expect-error - is private expect(client.socket).toBeUndefined(); } }); }); describe('#compare()', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should return true if entry has the specified attribute and value', async () => { const result = await client.compare(`uid=user1,${BASE_DN}`, 'sn', 'SURNAME'); expect(result).toBe(true); }); it('should return false if entry does not have the specified attribute and value', async () => { const result = await client.compare(`uid=user1,${BASE_DN}`, 'sn', 'Stark'); expect(result).toBe(false); }); it('should throw if attribute is invalid', async () => { await expect(client.compare(`uid=user1,${BASE_DN}`, 'lorem', 'ipsum')).rejects.toBeInstanceOf(UndefinedTypeError); }); it('should throw if target dn does not exist', async () => { await expect(client.compare(`uid=foo.bar,${BASE_DN}`, 'uid', 'bruce.banner')).rejects.toBeInstanceOf(NoSuchObjectError); }); it('should throw on invalid DN', async () => { await expect(client.compare('foo=bar', 'cn', 'bar')).rejects.toBeInstanceOf(InvalidDNSyntaxError); }); }); describe('#modify()', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should allow replacing an attribute', async () => { await client.modify( `uid=user4,${BASE_DN}`, new Change({ operation: 'replace', modification: new Attribute({ type: 'mail', values: [`four@${LDAP_DOMAIN}`], }), }), ); const { searchEntries } = await client.search(BASE_DN, { filter: `uid=user4`, attributes: ['mail'], }); expect(searchEntries).toHaveLength(1); expect(searchEntries[0]).toHaveProperty('mail', `four@${LDAP_DOMAIN}`); // change it back to user4@LDAP_DOMAIN await client.modify( `uid=user4,${BASE_DN}`, new Change({ operation: 'replace', modification: new Attribute({ type: 'mail', values: [`user4@${LDAP_DOMAIN}`], }), }), ); const { searchEntries: searchEntries2 } = await client.search(BASE_DN, { filter: `uid=user4`, attributes: ['mail'], }); expect(searchEntries2).toHaveLength(1); expect(searchEntries2[0]).toHaveProperty('mail', `user4@${LDAP_DOMAIN}`); }); it('should allow pushing onto attributes', async () => { await client.modify( `uid=user4,${BASE_DN}`, new Change({ operation: 'add', modification: new Attribute({ type: 'mail', values: [`four@${LDAP_DOMAIN}`], }), }), ); const { searchEntries } = await client.search(BASE_DN, { filter: `uid=user4`, attributes: ['mail'], }); expect(searchEntries).toHaveLength(1); expect(searchEntries[0]?.['mail']).toContain(`four@${LDAP_DOMAIN}`); }); it('should allow removing an attribute', async () => { await client.modify( `uid=user4,${BASE_DN}`, new Change({ operation: 'delete', modification: new Attribute({ type: 'mail', values: [`four@${LDAP_DOMAIN}`], }), }), ); const { searchEntries } = await client.search(BASE_DN, { filter: `uid=user4`, attributes: ['mail'], }); expect(searchEntries).toHaveLength(1); expect(searchEntries[0]).toHaveProperty('mail', `user4@${LDAP_DOMAIN}`); }); it('should allow updating binary attributes', async () => { // this should be a 10x10 green square PNG // we're putting it in an attribute called jpegPhoto but that doesn't matter for the test const jpegPhotoBuffer = Buffer.from('iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAFUlEQVR42mNk+M9Qz0AEYBxVSF+FAAhKDveksOjmAAAAAElFTkSuQmCC', 'base64'); await client.modify( `uid=user4,${BASE_DN}`, new Change({ operation: 'replace', modification: new Attribute({ type: 'jpegPhoto', values: [jpegPhotoBuffer], }), }), ); const { searchEntries: searchEntries2 } = await client.search(BASE_DN, { filter: `uid=user4`, attributes: ['jpegPhoto'], }); expect(searchEntries2).toHaveLength(1); expect(searchEntries2[0]?.['jpegPhoto']).toStrictEqual(jpegPhotoBuffer); }); }); describe('#add()', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should allow adding entry with null or undefined attribute value. Issue #88', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const stub = vi.spyOn(client as any, '_send').mockResolvedValue( new AddResponse({ messageId: 123, }), ); await client.add(`uid=reed.richards,${BASE_DN}`, { // @ts-expect-error - Test data userPassword: null, // @ts-expect-error - Test data foo: undefined, }); expect(stub).toHaveBeenCalledOnce(); const args = stub.mock.calls[0]![0] as AddRequest; expect(args.attributes).toStrictEqual([ new Attribute({ type: 'userPassword', values: [], }), new Attribute({ type: 'foo', values: [], }), ]); stub.mockRestore(); }); }); describe('#modifyDN()', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should set newSuperior when newDN is a string and contains a comma', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const stub = vi.spyOn(client as any, '_send').mockResolvedValue( new ModifyDNResponse({ messageId: 123, }), ); const dn = 'uid=groot,ou=Users,dc=foo,dc=com'; const newRdn = 'uid=new-groot'; const newSuperior = 'ou=Users,dc=foo,dc=com'; const newDN = `${newRdn},${newSuperior}`; await client.modifyDN(dn, newDN); expect(stub).toHaveBeenCalledOnce(); const args = stub.mock.calls[0]![0] as ModifyDNRequest; expect(args.dn).toBe(dn); expect(args.deleteOldRdn).toBe(true); expect(args.newRdn).toBe(newRdn); expect(args.newSuperior).toBe(newSuperior); expect(args.controls).toBeUndefined(); stub.mockRestore(); }); it('should handle escaped comma in newDN. Issue #87', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const stub = vi.spyOn(client as any, '_send').mockResolvedValue( new ModifyDNResponse({ messageId: 123, }), ); const dn = 'uid=groot,ou=Users,dc=foo,dc=com'; const newRdn = 'uid=new\\,groot'; const newSuperior = 'ou=Users,dc=foo,dc=com'; const newDN = `${newRdn},${newSuperior}`; await client.modifyDN(dn, newDN); expect(stub).toHaveBeenCalledOnce(); const args = stub.mock.calls[0]![0] as ModifyDNRequest; expect(args.dn).toBe(dn); expect(args.deleteOldRdn).toBe(true); expect(args.newRdn).toBe(newRdn); expect(args.newSuperior).toBe(newSuperior); expect(args.controls).toBeUndefined(); stub.mockRestore(); }); it('should handle newSuperior with the long form', async () => { // eslint-disable-next-line @typescript-eslint/no-explicit-any const stub = vi.spyOn(client as any, '_send').mockResolvedValue( new ModifyDNResponse({ messageId: 123, }), ); const dn = 'uid=groot,ou=Users,dc=foo,dc=com'; const newRdn = 'uid=groot'; const newSuperior = 'OU=O|10006677|重新命名选择权测试部门3,OU=O|10006677|重新命名选择权测试部门2,OU=O|10006677|重新命名选择权测试部门1,OU=O|10006677|重新命名选择权测试部门,OU=O|10002220|重新命名选择权,OU=重新命名选择权中心测试,ou=Users,dc=foo,dc=com'; const newDN = `${newRdn},${newSuperior}`; await client.modifyDN(dn, newDN); expect(stub).toHaveBeenCalledOnce(); const args = stub.mock.calls[0]![0] as ModifyDNRequest; expect(args.dn).toBe(dn); expect(args.deleteOldRdn).toBe(true); expect(args.newRdn).toBe(newRdn); expect(args.newSuperior).toBe(newSuperior); expect(args.controls).toBeUndefined(); stub.mockRestore(); }); }); describe('#exop()', () => { it('should throw if fast bind is not supported', async () => { const client: Client = new Client({ url: LDAP_URI, }); await expect(client.exop('1.2.840.113556.1.4.1781')).rejects.toThrow('unsupported extended operation Code: 0x2'); await client.bind(BIND_DN, BIND_PW); await client.unbind(); }); }); describe('#search()', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should return search entries with (objectclass=*) if no filter is specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(objectclass=*)" const searchResult = await client.search(BASE_DN); expect(searchResult.searchEntries.length).toBeGreaterThan(0); }); it('should throw error if an operation is performed after the client has closed connection', async () => { const testClient = new Client({ url: LDAP_URI, }); try { await testClient.bind(BIND_DN, BIND_PW); const unbindRequest = testClient.unbind(); const searchRequest = testClient.search(BASE_DN); await unbindRequest; await expect(searchRequest).rejects.toThrow('Connection closed before message response was received. Message type: SearchRequest (0x63)'); } finally { await testClient.unbind(); } }); it('should return full search entries if filter="(mail=user1@ldap.local)"', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(mail=peter.parker@marvel.com)" const searchResult = await client.search(BASE_DN, { filter: `(mail=user1@${LDAP_DOMAIN})`, }); expect(searchResult.searchEntries).toStrictEqual([ { cn: 'user1', dn: 'uid=user1,dc=ldap,dc=local', gidNumber: '14564100', homeDirectory: '/home/user', loginShell: '/bin/bash', mail: 'user1@ldap.local', objectClass: ['top', 'posixAccount', 'inetOrgPerson'], sn: 'SURNAME', uid: 'user1', uidNumber: '14583101', userPassword: '{SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA=', }, ]); }); it('should return full search entries if filter="(mail=user1*)"', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(mail=peter.parker@marvel.com)" const searchResult = await client.search(BASE_DN, { filter: '(mail=user1*)', }); expect(searchResult.searchEntries).toStrictEqual([ { cn: 'user1', dn: 'uid=user1,dc=ldap,dc=local', gidNumber: '14564100', homeDirectory: '/home/user', loginShell: '/bin/bash', mail: 'user1@ldap.local', objectClass: ['top', 'posixAccount', 'inetOrgPerson'], sn: 'SURNAME', uid: 'user1', uidNumber: '14583101', userPassword: '{SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA=', }, ]); }); it('should return parallel search entries if filter="(mail=user1@ldap.local)". Issue #83', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(mail=peter.parker@marvel.com)" const [result1, result2, result3] = await Promise.all([ client.search(BASE_DN, { filter: `(mail=user1@${LDAP_DOMAIN})`, }), client.search(BASE_DN, { filter: `(mail=user1@${LDAP_DOMAIN})`, }), client.search(BASE_DN, { filter: `(mail=user1@${LDAP_DOMAIN})`, }), ]); const expectedResult = [ { cn: 'user1', dn: 'uid=user1,dc=ldap,dc=local', gidNumber: '14564100', homeDirectory: '/home/user', loginShell: '/bin/bash', mail: 'user1@ldap.local', objectClass: ['top', 'posixAccount', 'inetOrgPerson'], sn: 'SURNAME', uid: 'user1', uidNumber: '14583101', userPassword: '{SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA=', }, ]; expect(result1.searchEntries).toStrictEqual(expectedResult); expect(result2.searchEntries).toStrictEqual(expectedResult); expect(result3.searchEntries).toStrictEqual(expectedResult); }); it('should allow arbitrary controls 1.2.840.113556.1.4.417 to be specified', async () => { const searchResult = await client.search( BASE_DN, { scope: 'sub', filter: '(isDeleted=*)', }, new Control('1.2.840.113556.1.4.417'), ); expect(searchResult.searchEntries.length).toBe(0); }); it('should restrict attributes returned if attributes are specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(mail=peter.parker@marvel.com)" "cn" const searchResult = await client.search(BASE_DN, { scope: 'sub', filter: `(mail=user1@${LDAP_DOMAIN})`, attributes: ['cn'], }); expect(searchResult.searchEntries).toStrictEqual([ { dn: `uid=user1,${BASE_DN}`, cn: 'user1', }, ]); }); it('should include attributes without values if attributes are specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(mail=peter.parker@marvel.com)" "cn" const searchResult = await client.search(BASE_DN, { scope: 'sub', filter: `(mail=user1@${LDAP_DOMAIN})`, attributes: ['cn', 'telephoneNumber'], }); expect(searchResult.searchEntries).toStrictEqual([ { dn: `uid=user1,${BASE_DN}`, cn: 'user1', telephoneNumber: [], }, ]); }); it('should return clean list of attributes even if the requested attribute is in the wrong case', async () => { const searchResult = await client.search(BASE_DN, { scope: 'sub', filter: `(mail=user1@${LDAP_DOMAIN})`, attributes: ['homedirectory'], }); expect(searchResult.searchEntries).toStrictEqual([ { dn: `uid=user1,${BASE_DN}`, homeDirectory: '/home/user', }, ]); }); it('should not return attribute values if returnAttributeValues=false', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm -A "(mail=peter.parker@marvel.com)" const searchResult = await client.search(BASE_DN, { scope: 'sub', filter: `(mail=user1@${LDAP_DOMAIN})`, returnAttributeValues: false, }); expect(searchResult.searchEntries).toStrictEqual([ { cn: [], dn: 'uid=user1,dc=ldap,dc=local', gidNumber: [], homeDirectory: [], loginShell: [], mail: [], objectClass: [], sn: [], uid: [], uidNumber: [], userPassword: [], }, ]); }); it('should page search entries if paging is specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm -E pr=2/noprompt "objectClass=jumpcloudUser" const searchResult = await client.search(BASE_DN, { filter: 'objectClass=*', paged: { pageSize: 2, }, }); expect(searchResult.searchEntries.length).toBeGreaterThan(2); }); it('should allow sizeLimit when no paging is specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm -z 6 'cn=*' const searchResult = await client.search(BASE_DN, { filter: 'cn=*', sizeLimit: 3, }); expect(searchResult.searchEntries.length).toBe(3); }); it('should allow sizeLimit when paging is specified', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm -E pr=3/noprompt -z 5 'cn=*' const searchResult = await client.search(BASE_DN, { filter: 'cn=*', sizeLimit: 5, paged: { pageSize: 3, }, }); expect(searchResult.searchEntries.length).toBe(5); }); it('should return group contents with parenthesis in name - explicit filter controls', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(&(objectClass=groupOfNames)(cn=Something \28Special\29))" const searchResult = await client.search(BASE_DN, { filter: new AndFilter({ filters: [ new EqualityFilter({ attribute: 'objectClass', value: 'posixGroup', }), new EqualityFilter({ attribute: 'cn', value: 'UserGroup2 (Test)', }), ], }), }); expect(searchResult.searchEntries).toStrictEqual([ { cn: 'UserGroup2 (Test)', dn: 'cn=UserGroup2 (Test),dc=ldap,dc=local', gidNumber: '2539', memberUid: ['user3', 'user2'], objectClass: ['posixGroup', 'top'], }, ]); }); it('should return group contents with parenthesis in name - string filter', async () => { // NOTE: ldapsearch -H ldaps://localhost:636 -b o=5be4c382c583e54de6a3ff52,dc=jumpcloud,dc=com -x -D uid=tony.stark,dc=jumpcloud,dc=com -w MyRedSuitKeepsMeWarm "(&(objectClass=groupOfNames)(cn=Something \28Special\29))" const searchResult = await client.search(BASE_DN, { filter: '(&(objectClass=posixGroup)(cn=UserGroup2 \\28Test\\29))', }); expect(searchResult.searchEntries).toStrictEqual([ { cn: 'UserGroup2 (Test)', dn: 'cn=UserGroup2 (Test),dc=ldap,dc=local', gidNumber: '2539', memberUid: ['user3', 'user2'], objectClass: ['posixGroup', 'top'], }, ]); }); it('should throw if a PagedResultsControl is specified', async () => { const pagedResultsControl = new PagedResultsControl({}); await expect(client.search('cn=test', {}, pagedResultsControl)).rejects.toThrow('Should not specify PagedResultsControl'); }); it('should throw if a PagedResultsControl is specified in the controls array', async () => { const pagedResultsControl = new PagedResultsControl({}); await expect(client.search('cn=test', {}, [pagedResultsControl])).rejects.toThrow('Should not specify PagedResultsControl'); }); }); describe('#searchPaginated', () => { const client: Client = new Client({ url: LDAP_URI, }); beforeAll(async () => { await client.bind(BIND_DN, BIND_PW); }); afterAll(async () => { await client.unbind(); }); it('should paginate', async () => { const pageSize = 10; const paginator = client.searchPaginated(BASE_DN, { filter: 'objectclass=*', paged: { pageSize, }, }); let totalResults = 0; let iterateCount = 0; for await (const searchResult of paginator) { iterateCount++; totalResults += searchResult.searchEntries.length; expect(searchResult.searchEntries.length).toBeLessThanOrEqual(pageSize); } expect(iterateCount).toBeGreaterThanOrEqual(1); expect(totalResults / iterateCount).toBeLessThanOrEqual(pageSize); }); }); describe('#disposable', () => { it('should unbind after disposed', async () => { const spy = vi.fn<(client: Client, method: string) => void>(); try { await using client = new Client({ url: LDAP_URI, }); spy(client, 'unbind'); await client.bind(BIND_DN, BIND_PW); } catch { /* empty */ } finally { expect(spy).toHaveBeenCalledOnce(); } }); it('should destroy socket after disposed', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); await client[Symbol.asyncDispose](); // @ts-expect-error - is private expect(client.socket).toBeUndefined(); }); it('should destroy socket after connection failure', async () => { const client = new Client({ connectTimeout: 300, url: 'ldap://localhost:9999', }); try { // @ts-expect-error - is private await client._connect(); } catch { // ignore } finally { // @ts-expect-error - is private expect(client.connectTimer).toBeUndefined(); // @ts-expect-error - is private expect(client.socket).toBeUndefined(); } }); it('should destroy socket after tls connection failure', async () => { const client = new Client({ connectTimeout: 5000, url: 'ldaps://localhost:389', }); try { // @ts-expect-error - is private await client._connect(); } catch { // ignore } finally { // @ts-expect-error - is private expect(client.connectTimer).toBeUndefined(); // @ts-expect-error - is private expect(client.socket).toBeUndefined(); } }); it('should clear timeouts after message resolved/rejected', async () => { const client = new Client({ timeout: 5000, connectTimeout: 3000, url: LDAP_URI, }); // @ts-expect-error - it is private const messageMap = client.messageDetailsByMessageId; const messageMapSetter = vi.spyOn(messageMap, 'set'); try { await client.bind(BIND_DN, BIND_PW); } catch { /* empty */ } finally { expect(messageMapSetter).toHaveBeenCalledOnce(); // eslint-disable-next-line @typescript-eslint/no-explicit-any const setCalls = messageMapSetter.mock.calls as any[]; expect(setCalls[0][0]).toBeDefined(); expect(setCalls[0][1].timeoutTimer).toBeTruthy(); expect(messageMap.size).toBe(0); } }); }); }); ldapts-ldapts-91d5f4e/tests/Controls.test.ts000066400000000000000000000032071522446626000212150ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { Client, ServerSideSortingRequestControl } from '../src/index.js'; process.env['NODE_TLS_REJECT_UNAUTHORIZED'] = '0'; const LDAP_URI = 'ldap://localhost:389'; const BASE_DN = 'dc=ldap,dc=local'; const BIND_DN = `cn=admin,${BASE_DN}`; const BIND_PW = '1234'; describe('Controls', () => { describe('ServerSideSortingRequestControl', () => { it('should sort values', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); const res = await client.search( BASE_DN, { filter: 'uid=*', attributes: ['uid'], }, new ServerSideSortingRequestControl({ value: { reverseOrder: false, attributeType: 'uid', orderingRule: 'caseIgnoreOrderingMatch' }, }), ); const results = res.searchEntries.map((entry) => entry['uid']) as [string, ...string[]]; expect(results[0]).toBe(results.sort()[0]); }); it('should sort values (descending)', async () => { const client = new Client({ url: LDAP_URI, }); await client.bind(BIND_DN, BIND_PW); const res = await client.search( BASE_DN, { filter: 'uid=*', attributes: ['uid'], }, new ServerSideSortingRequestControl({ value: { reverseOrder: true, attributeType: 'uid', orderingRule: 'caseIgnoreOrderingMatch' }, }), ); const results = res.searchEntries.map((entry) => entry['uid']) as [string, ...string[]]; expect(results[0]).toBe(results.sort().reverse()[0]); }); }); }); ldapts-ldapts-91d5f4e/tests/FilterParser.test.ts000066400000000000000000001356471522446626000220320ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { AndFilter, ApproximateFilter, EqualityFilter, ExtensibleFilter, FilterParser, GreaterThanEqualsFilter, LessThanEqualsFilter, NotFilter, OrFilter, PresenceFilter, SubstringFilter, } from '../src/index.js'; describe('FilterParser', () => { describe('#parseString()', () => { it('should throw for empty filters', () => { expect((): void => { FilterParser.parseString(''); }).toThrow('Filter cannot be empty'); }); it('should throw if parenthesis are unbalanced', () => { expect((): void => { FilterParser.parseString('(cn=foo'); }).toThrow('Unbalanced parens'); }); it('should throw for invalid filter', () => { expect((): void => { FilterParser.parseString('foo>bar'); }).toThrow('Invalid expression: foo>bar'); }); it('should handle non-wrapped filters', () => { expect(FilterParser.parseString('cn=foo')).toStrictEqual( new EqualityFilter({ attribute: 'cn', value: 'foo', }), ); }); it('should throw for only parenthesis', () => { expect((): void => { FilterParser.parseString('()'); }).toThrow('Invalid attribute name:'); }); it('should throw for nested parenthesis', () => { expect((): void => { FilterParser.parseString('((cn=foo))'); }).toThrow('Invalid attribute name: (cn=foo'); }); it('should allow xml in filter string', () => { const result = FilterParser.parseString('(&(CentralUIEnrollments=*)(objectClass=User))'); expect(result).toStrictEqual( new AndFilter({ filters: [ new SubstringFilter({ attribute: 'CentralUIEnrollments', initial: '', }), new EqualityFilter({ attribute: 'objectClass', value: 'User', }), ], }), ); }); describe('Special characters in filter string', () => { it('should allow = in filter string', () => { const result = FilterParser.parseString('(uniquemember=uuid=930896af-bf8c-48d4-885c-6573a94b1853, ou=users, o=smartdc)'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'uniquemember', value: 'uuid=930896af-bf8c-48d4-885c-6573a94b1853, ou=users, o=smartdc', }), ); }); describe('paren in value', () => { it('should allow ( in filter string', () => { const result = FilterParser.parseString('foo=bar\\28'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar(', }), ); }); it('should allow ) in filter string', () => { const result = FilterParser.parseString('foo=bar\\29'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar)', }), ); }); it('should allow () in filter string', () => { const result = FilterParser.parseString('foo=bar\\28\\29'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar()', }), ); }); it('should allow )( in filter string', () => { const result = FilterParser.parseString('foo=bar\\29\\28'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar)(', }), ); }); }); describe('newline in value', () => { it('should allow newline as attribute value', () => { expect(FilterParser.parseString('(foo=\n)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\n', }), ); expect(FilterParser.parseString('(foo<=\n)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\n', }), ); expect(FilterParser.parseString('(foo>=\n)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\n', }), ); expect(FilterParser.parseString('(foo=\\0a)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\n', }), ); expect(FilterParser.parseString('(foo<=\\0a)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\n', }), ); expect(FilterParser.parseString('(foo>=\\0a)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\n', }), ); }); it('should allow newline after attribute value', () => { expect(FilterParser.parseString('(foo=bar\n)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\n', }), ); expect(FilterParser.parseString('(foo<=bar\n)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\n', }), ); expect(FilterParser.parseString('(foo>=bar\n)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\n', }), ); expect(FilterParser.parseString('(foo=bar\\0a)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\n', }), ); expect(FilterParser.parseString('(foo<=bar\\0a)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\n', }), ); expect(FilterParser.parseString('(foo>=bar\\0a)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\n', }), ); }); it('should allow newline before attribute value', () => { expect(FilterParser.parseString('(foo=\nbar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\nbar', }), ); expect(FilterParser.parseString('(foo<=\nbar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\nbar', }), ); expect(FilterParser.parseString('(foo>=\nbar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\nbar', }), ); expect(FilterParser.parseString('(foo=\\0abar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\nbar', }), ); expect(FilterParser.parseString('(foo<=\\0abar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\nbar', }), ); expect(FilterParser.parseString('(foo>=\\0abar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\nbar', }), ); }); it('should allow carriage return as attribute value', () => { expect(FilterParser.parseString('(foo=\r)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\r', }), ); expect(FilterParser.parseString('(foo<=\r)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\r', }), ); expect(FilterParser.parseString('(foo>=\r)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\r', }), ); expect(FilterParser.parseString('(foo=\\0d)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\r', }), ); expect(FilterParser.parseString('(foo<=\\0d)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\r', }), ); expect(FilterParser.parseString('(foo>=\\0d)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\r', }), ); }); it('should allow carriage return after attribute value', () => { expect(FilterParser.parseString('(foo=bar\r)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\r', }), ); expect(FilterParser.parseString('(foo<=bar\r)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\r', }), ); expect(FilterParser.parseString('(foo>=bar\r)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\r', }), ); expect(FilterParser.parseString('(foo=bar\\0d)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\r', }), ); expect(FilterParser.parseString('(foo<=bar\\0d)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\r', }), ); expect(FilterParser.parseString('(foo>=bar\\0d)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\r', }), ); }); it('should allow carriage return before attribute value', () => { expect(FilterParser.parseString('(foo=\rbar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\rbar', }), ); expect(FilterParser.parseString('(foo<=\rbar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\rbar', }), ); expect(FilterParser.parseString('(foo>=\rbar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\rbar', }), ); expect(FilterParser.parseString('(foo=\\0dbar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\rbar', }), ); expect(FilterParser.parseString('(foo<=\\0dbar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\rbar', }), ); expect(FilterParser.parseString('(foo>=\\0dbar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\rbar', }), ); }); }); describe('tab in value', () => { it('should allow tab as attribute value', () => { expect(FilterParser.parseString('(foo=\t)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\t', }), ); expect(FilterParser.parseString('(foo<=\t)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\t', }), ); expect(FilterParser.parseString('(foo>=\t)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\t', }), ); expect(FilterParser.parseString('(foo=\\09)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\t', }), ); expect(FilterParser.parseString('(foo<=\\09)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\t', }), ); expect(FilterParser.parseString('(foo>=\\09)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\t', }), ); }); it('should allow tab after attribute value', () => { expect(FilterParser.parseString('(foo=bar\t)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\t', }), ); expect(FilterParser.parseString('(foo<=bar\t)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\t', }), ); expect(FilterParser.parseString('(foo>=bar\t)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\t', }), ); expect(FilterParser.parseString('(foo=bar\\09)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\t', }), ); expect(FilterParser.parseString('(foo<=bar\\09)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\t', }), ); expect(FilterParser.parseString('(foo>=bar\\09)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\t', }), ); }); it('should allow tab before attribute value', () => { expect(FilterParser.parseString('(foo=\tbar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\tbar', }), ); expect(FilterParser.parseString('(foo<=\tbar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\tbar', }), ); expect(FilterParser.parseString('(foo>=\tbar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\tbar', }), ); expect(FilterParser.parseString('(foo=\\09bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\tbar', }), ); expect(FilterParser.parseString('(foo<=\\09bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\tbar', }), ); expect(FilterParser.parseString('(foo>=\\09bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\tbar', }), ); }); }); describe('space in value', () => { it('should allow space as attribute value', () => { expect(FilterParser.parseString('(foo= )')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: ' ', }), ); expect(FilterParser.parseString('(foo<= )')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: ' ', }), ); expect(FilterParser.parseString('(foo>= )')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: ' ', }), ); expect(FilterParser.parseString('(foo=\\20)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: ' ', }), ); expect(FilterParser.parseString('(foo<=\\20)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: ' ', }), ); expect(FilterParser.parseString('(foo>=\\20)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: ' ', }), ); }); it('should allow space after attribute value', () => { expect(FilterParser.parseString('(foo=bar )')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar ', }), ); expect(FilterParser.parseString('(foo<=bar )')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar ', }), ); expect(FilterParser.parseString('(foo>=bar )')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar ', }), ); expect(FilterParser.parseString('(foo=bar\\20)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar ', }), ); expect(FilterParser.parseString('(foo<=bar\\20)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar ', }), ); expect(FilterParser.parseString('(foo>=bar\\20)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar ', }), ); }); it('should allow space before attribute value', () => { expect(FilterParser.parseString('(foo= bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: ' bar', }), ); expect(FilterParser.parseString('(foo<= bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: ' bar', }), ); expect(FilterParser.parseString('(foo>= bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: ' bar', }), ); expect(FilterParser.parseString('(foo=\\20bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: ' bar', }), ); expect(FilterParser.parseString('(foo<=\\20bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: ' bar', }), ); expect(FilterParser.parseString('(foo>=\\20bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: ' bar', }), ); }); }); describe('\\ in value', () => { it('should allow \\ as attribute value', () => { expect(FilterParser.parseString('(foo=\\5c)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\\', }), ); expect(FilterParser.parseString('(foo<=\\5c)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\\', }), ); expect(FilterParser.parseString('(foo>=\\5c)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\\', }), ); }); it('should allow \\ after attribute value', () => { expect(FilterParser.parseString('(foo=bar\\5c)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\\', }), ); expect(FilterParser.parseString('(foo<=bar\\5c)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar\\', }), ); expect(FilterParser.parseString('(foo>=bar\\5c)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar\\', }), ); }); it('should allow \\ before attribute value', () => { expect(FilterParser.parseString('(foo=\\5cbar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\\bar', }), ); expect(FilterParser.parseString('(foo<=\\5cbar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\\bar', }), ); expect(FilterParser.parseString('(foo>=\\5cbar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\\bar', }), ); }); it('should allow \\ in attribute value', () => { expect(FilterParser.parseString('(foo=\\5cbar\\5cbaz\\5c)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '\\bar\\baz\\', }), ); expect(FilterParser.parseString('(foo<=\\5cbar\\5cbaz\\5c)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '\\bar\\baz\\', }), ); expect(FilterParser.parseString('(foo>=\\5cbar\\5cbaz\\5c)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '\\bar\\baz\\', }), ); }); it('should allow null (\\00) value in attribute value', () => { expect(FilterParser.parseString('(foo=bar\\00)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar\u0000', }), ); }); }); describe('* in value', () => { it('should allow * as attribute value', () => { expect(FilterParser.parseString('(foo=\\2a)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '*', }), ); expect(FilterParser.parseString('(foo<=\\2a)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '*', }), ); expect(FilterParser.parseString('(foo>=\\2a)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '*', }), ); }); it('should allow * after attribute value', () => { expect(FilterParser.parseString('(foo=bar\\2a)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar*', }), ); expect(FilterParser.parseString('(foo<=bar\\2a)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar*', }), ); expect(FilterParser.parseString('(foo>=bar\\2a)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar*', }), ); }); it('should allow * before attribute value', () => { expect(FilterParser.parseString('(foo=\\2abar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '*bar', }), ); expect(FilterParser.parseString('(foo<=\\2abar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '*bar', }), ); expect(FilterParser.parseString('(foo>=\\2abar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '*bar', }), ); }); it('should allow * in attribute value', () => { expect(FilterParser.parseString('(foo=\\2abar\\2abaz\\2a)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '*bar*baz*', }), ); expect(FilterParser.parseString('(foo<=\\2abar\\2abaz\\2a)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '*bar*baz*', }), ); expect(FilterParser.parseString('(foo>=\\2abar\\2abaz\\2a)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '*bar*baz*', }), ); }); }); describe('<= in value', () => { it('should allow <= as attribute value', () => { expect(FilterParser.parseString('(foo=<=)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '<=', }), ); expect(FilterParser.parseString('(foo<=<=)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '<=', }), ); expect(FilterParser.parseString('(foo>=<=)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '<=', }), ); }); it('should allow <= after attribute value', () => { expect(FilterParser.parseString('(foo=bar<=)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar<=', }), ); expect(FilterParser.parseString('(foo<=bar<=)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar<=', }), ); expect(FilterParser.parseString('(foo>=bar<=)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar<=', }), ); }); it('should allow <= before attribute value', () => { expect(FilterParser.parseString('(foo=<=bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '<=bar', }), ); expect(FilterParser.parseString('(foo<=<=bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '<=bar', }), ); expect(FilterParser.parseString('(foo>=<=bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '<=bar', }), ); }); it('should allow <= in attribute value', () => { expect(FilterParser.parseString('(foo=bar<=baz)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar<=baz', }), ); expect(FilterParser.parseString('(foo<=bar<=baz)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar<=baz', }), ); expect(FilterParser.parseString('(foo>=bar<=baz)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar<=baz', }), ); }); }); describe('>= in value', () => { it('should allow >= as attribute value', () => { expect(FilterParser.parseString('(foo=>=)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '>=', }), ); expect(FilterParser.parseString('(foo<=>=)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '>=', }), ); expect(FilterParser.parseString('(foo>=>=)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '>=', }), ); }); it('should allow >= after attribute value', () => { expect(FilterParser.parseString('(foo=bar>=)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar>=', }), ); expect(FilterParser.parseString('(foo<=bar>=)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar>=', }), ); expect(FilterParser.parseString('(foo>=bar>=)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar>=', }), ); }); it('should allow >= before attribute value', () => { expect(FilterParser.parseString('(foo=>=bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '>=bar', }), ); expect(FilterParser.parseString('(foo<=>=bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '>=bar', }), ); expect(FilterParser.parseString('(foo>=>=bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '>=bar', }), ); }); it('should allow >= in attribute value', () => { expect(FilterParser.parseString('(foo=bar>=baz)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar>=baz', }), ); expect(FilterParser.parseString('(foo<=bar>=baz)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar>=baz', }), ); expect(FilterParser.parseString('(foo>=bar>=baz)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar>=baz', }), ); }); }); describe('& in value', () => { it('should allow & as attribute value', () => { expect(FilterParser.parseString('(foo=&)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '&', }), ); expect(FilterParser.parseString('(foo<=&)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '&', }), ); expect(FilterParser.parseString('(foo>=&)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '&', }), ); }); it('should allow & after attribute value', () => { expect(FilterParser.parseString('(foo=bar&)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar&', }), ); expect(FilterParser.parseString('(foo<=bar&)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar&', }), ); expect(FilterParser.parseString('(foo>=bar&)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar&', }), ); }); it('should allow & before attribute value', () => { expect(FilterParser.parseString('(foo=&bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '&bar', }), ); expect(FilterParser.parseString('(foo<=&bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '&bar', }), ); expect(FilterParser.parseString('(foo>=&bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '&bar', }), ); }); it('should allow & in attribute value', () => { expect(FilterParser.parseString('(foo=&bar&baz&)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '&bar&baz&', }), ); expect(FilterParser.parseString('(foo<=&bar&baz&)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '&bar&baz&', }), ); expect(FilterParser.parseString('(foo>=&bar&baz&)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '&bar&baz&', }), ); }); }); describe('| in value', () => { it('should allow | as attribute value', () => { expect(FilterParser.parseString('(foo=|)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '|', }), ); expect(FilterParser.parseString('(foo<=|)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '|', }), ); expect(FilterParser.parseString('(foo>=|)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '|', }), ); }); it('should allow | after attribute value', () => { expect(FilterParser.parseString('(foo=bar|)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar|', }), ); expect(FilterParser.parseString('(foo<=bar|)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar|', }), ); expect(FilterParser.parseString('(foo>=bar|)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar|', }), ); }); it('should allow | before attribute value', () => { expect(FilterParser.parseString('(foo=|bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '|bar', }), ); expect(FilterParser.parseString('(foo<=|bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '|bar', }), ); expect(FilterParser.parseString('(foo>=|bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '|bar', }), ); }); it('should allow | in attribute value', () => { expect(FilterParser.parseString('(foo=|bar|baz|)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '|bar|baz|', }), ); expect(FilterParser.parseString('(foo<=|bar|baz|)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '|bar|baz|', }), ); expect(FilterParser.parseString('(foo>=|bar|baz|)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '|bar|baz|', }), ); }); }); describe('! in value', () => { it('should allow ! as attribute value', () => { expect(FilterParser.parseString('(foo=!)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '!', }), ); expect(FilterParser.parseString('(foo<=!)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '!', }), ); expect(FilterParser.parseString('(foo>=!)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '!', }), ); }); it('should allow ! after attribute value', () => { expect(FilterParser.parseString('(foo=bar!)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'bar!', }), ); expect(FilterParser.parseString('(foo<=bar!)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'bar!', }), ); expect(FilterParser.parseString('(foo>=bar!)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'bar!', }), ); }); it('should allow ! before attribute value', () => { expect(FilterParser.parseString('(foo=!bar)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '!bar', }), ); expect(FilterParser.parseString('(foo<=!bar)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '!bar', }), ); expect(FilterParser.parseString('(foo>=!bar)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '!bar', }), ); }); it('should allow ! in attribute value', () => { expect(FilterParser.parseString('(foo=!bar!baz!)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '!bar!baz!', }), ); expect(FilterParser.parseString('(foo<=!bar!baz!)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '!bar!baz!', }), ); expect(FilterParser.parseString('(foo>=!bar!baz!)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '!bar!baz!', }), ); }); }); describe('unicode in value', () => { it('should allow ☕⛵ᄨ as attribute value', () => { expect(FilterParser.parseString('(foo=☕⛵ᄨ)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: '☕⛵ᄨ', }), ); expect(FilterParser.parseString('(foo<=☕⛵ᄨ)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: '☕⛵ᄨ', }), ); expect(FilterParser.parseString('(foo>=☕⛵ᄨ)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: '☕⛵ᄨ', }), ); }); it('should allow ᎢᏣᎵᏍᎠᏁᏗ as attribute value', () => { expect(FilterParser.parseString('(foo=ᎢᏣᎵᏍᎠᏁᏗ)')).toStrictEqual( new EqualityFilter({ attribute: 'foo', value: 'ᎢᏣᎵᏍᎠᏁᏗ', }), ); expect(FilterParser.parseString('(foo<=ᎢᏣᎵᏍᎠᏁᏗ)')).toStrictEqual( new LessThanEqualsFilter({ attribute: 'foo', value: 'ᎢᏣᎵᏍᎠᏁᏗ', }), ); expect(FilterParser.parseString('(foo>=ᎢᏣᎵᏍᎠᏁᏗ)')).toStrictEqual( new GreaterThanEqualsFilter({ attribute: 'foo', value: 'ᎢᏣᎵᏍᎠᏁᏗ', }), ); }); }); describe('Tests from RFC examples', () => { it('should parse: (o=Parens R Us (for all your parenthetical needs))', () => { const result = FilterParser.parseString('(o=Parens R Us \\28for all your parenthetical needs\\29)'); expect(result).toStrictEqual( new EqualityFilter({ attribute: 'o', value: 'Parens R Us (for all your parenthetical needs)', }), ); }); it('should parse: (cn=***)', () => { const result = FilterParser.parseString('(cn=*\\2A*)'); expect(result).toStrictEqual( new SubstringFilter({ attribute: 'cn', any: ['*'], }), ); }); it('should parse: (&(objectCategory=group)(displayName=My group (something)))', () => { const result = FilterParser.parseString('(&(objectCategory=group)(displayName=My group \\28something\\29))'); expect(result).toStrictEqual( new AndFilter({ filters: [ new EqualityFilter({ attribute: 'objectCategory', value: 'group', }), new EqualityFilter({ attribute: 'displayName', value: 'My group (something)', }), ], }), ); }); }); }); describe('Github Issues', () => { it('should parse: (&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))', () => { const result = FilterParser.parseString('(&(objectCategory=person)(objectClass=user)(!(userAccountControl:1.2.840.113556.1.4.803:=2)))'); expect(result).toStrictEqual( new AndFilter({ filters: [ new EqualityFilter({ attribute: 'objectCategory', value: 'person', }), new EqualityFilter({ attribute: 'objectClass', value: 'user', }), new NotFilter({ filter: new ExtensibleFilter({ matchType: 'userAccountControl', rule: '1.2.840.113556.1.4.803', value: '2', }), }), ], }), ); }); }); describe('SubstringFilter', () => { it('should support * with a prefix', () => { const result = FilterParser.parseString('(foo=bar*)'); expect(result).toStrictEqual( new SubstringFilter({ attribute: 'foo', initial: 'bar', }), ); }); it('should support * with a suffix', () => { const result = FilterParser.parseString('(foo=*bar)'); expect(result).toStrictEqual( new SubstringFilter({ attribute: 'foo', final: 'bar', }), ); }); it('should support * with a prefix and escaped *', () => { const result = FilterParser.parseString('(foo=bar\\2a*)'); expect(result).toStrictEqual( new SubstringFilter({ attribute: 'foo', initial: 'bar*', }), ); }); it('should support * with a suffix and escaped *', () => { const result = FilterParser.parseString('(foo=*bar\\2a)'); expect(result).toStrictEqual( new SubstringFilter({ attribute: 'foo', final: 'bar*', }), ); }); }); describe('NotFilter', () => { it('should parse Not filter', () => { const result = FilterParser.parseString('(&(objectClass=person)(!(objectClass=shadowAccount)))'); expect(result).toStrictEqual( new AndFilter({ filters: [ new EqualityFilter({ attribute: 'objectClass', value: 'person', }), new NotFilter({ filter: new EqualityFilter({ attribute: 'objectClass', value: 'shadowAccount', }), }), ], }), ); }); }); describe('PresenceFilter', () => { it('should parse PresenceFilter', () => { const result = FilterParser.parseString('(foo=*)'); expect(result).toStrictEqual( new PresenceFilter({ attribute: 'foo', }), ); }); }); describe('OrFilter', () => { it('should parse PresenceFilter', () => { const result = FilterParser.parseString('(|(foo=bar)(baz=bip))'); expect(result).toStrictEqual( new OrFilter({ filters: [ new EqualityFilter({ attribute: 'foo', value: 'bar', }), new EqualityFilter({ attribute: 'baz', value: 'bip', }), ], }), ); }); }); describe('ApproximateFilter', () => { it('should parse ApproximateFilter', () => { const result = FilterParser.parseString('(foo~=bar)'); expect(result).toStrictEqual( new ApproximateFilter({ attribute: 'foo', value: 'bar', }), ); }); }); describe('ExtensibleFilter', () => { it('should parse: (cn:caseExactMatch:=Fred Flintstone)', () => { const result = FilterParser.parseString('(cn:caseExactMatch:=Fred Flintstone)'); expect(result).toStrictEqual( new ExtensibleFilter({ matchType: 'cn', rule: 'caseExactMatch', value: 'Fred Flintstone', }), ); }); it('should parse: (cn:=Betty Rubble)', () => { const result = FilterParser.parseString('(cn:=Betty Rubble)'); expect(result).toStrictEqual( new ExtensibleFilter({ matchType: 'cn', value: 'Betty Rubble', }), ); }); it('should parse: (sn:dn:2.4.6.8.10:=Barney Rubble)', () => { const result = FilterParser.parseString('(sn:dn:2.4.6.8.10:=Barney Rubble)'); expect(result).toStrictEqual( new ExtensibleFilter({ matchType: 'sn', rule: '2.4.6.8.10', dnAttributes: true, value: 'Barney Rubble', }), ); }); it('should parse: (o:dn:=Ace Industry)', () => { const result = FilterParser.parseString('(o:dn:=Ace Industry)'); expect(result).toStrictEqual( new ExtensibleFilter({ matchType: 'o', dnAttributes: true, value: 'Ace Industry', }), ); }); it('should parse: (:1.2.3:=Wilma Flintstone)', () => { const result = FilterParser.parseString('(:1.2.3:=Wilma Flintstone)'); expect(result).toStrictEqual( new ExtensibleFilter({ rule: '1.2.3', value: 'Wilma Flintstone', }), ); }); it('should parse: (:DN:2.4.6.8.10:=Dino)', () => { const result = FilterParser.parseString('(:DN:2.4.6.8.10:=Dino)'); expect(result).toStrictEqual( new ExtensibleFilter({ rule: '2.4.6.8.10', dnAttributes: true, value: 'Dino', }), ); }); }); }); }); ldapts-ldapts-91d5f4e/tests/PostalAddress.test.ts000066400000000000000000000063371522446626000221710ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { decodePostalAddress, encodePostalAddress } from '../src/PostalAddress.js'; describe('PostalAddress', () => { describe('#encodePostalAddress()', () => { it('should encode multiple lines with $ separator', () => { expect(encodePostalAddress('1640 Riverside Drive\nHill Valley, CA 91103\nUSA')).toBe('1640 Riverside Drive$Hill Valley, CA 91103$USA'); }); it('should encode single line without separator', () => { expect(encodePostalAddress('1640 Riverside Drive')).toBe('1640 Riverside Drive'); }); it('should escape $ characters', () => { expect(encodePostalAddress('Price: $100\nTotal')).toBe('Price: \\24100$Total'); }); it('should escape \\ characters', () => { expect(encodePostalAddress('Path: C:\\Users\nLocation')).toBe('Path: C:\\5CUsers$Location'); }); it('should escape both $ and \\ in same line', () => { expect(encodePostalAddress('Cost: $50\\item')).toBe('Cost: \\2450\\5Citem'); }); it('should handle empty lines', () => { expect(encodePostalAddress('Line 1\n\nLine 3')).toBe('Line 1$$Line 3'); }); it('should handle empty string', () => { expect(encodePostalAddress('')).toBe(''); }); it('should support custom separator', () => { expect(encodePostalAddress('Line 1|Line 2', '|')).toBe('Line 1$Line 2'); }); }); describe('#decodePostalAddress()', () => { it('should decode $ separated lines', () => { expect(decodePostalAddress('1640 Riverside Drive$Hill Valley, CA 91103$USA')).toBe('1640 Riverside Drive\nHill Valley, CA 91103\nUSA'); }); it('should decode single line', () => { expect(decodePostalAddress('1640 Riverside Drive')).toBe('1640 Riverside Drive'); }); it('should unescape \\24 to $', () => { expect(decodePostalAddress('Price: \\24100$Total')).toBe('Price: $100\nTotal'); }); it('should unescape \\5C to \\', () => { expect(decodePostalAddress('Path: C:\\5CUsers$Location')).toBe('Path: C:\\Users\nLocation'); }); it('should handle case-insensitive escape sequences', () => { expect(decodePostalAddress('\\5c\\5C\\24')).toBe('\\\\$'); }); it('should unescape both $ and \\ in same line', () => { expect(decodePostalAddress('Cost: \\2450\\5Citem')).toBe('Cost: $50\\item'); }); it('should handle empty lines', () => { expect(decodePostalAddress('Line 1$$Line 3')).toBe('Line 1\n\nLine 3'); }); it('should handle empty string', () => { expect(decodePostalAddress('')).toBe(''); }); it('should support custom separator', () => { expect(decodePostalAddress('Line 1$Line 2', '|')).toBe('Line 1|Line 2'); }); }); describe('round-trip', () => { it('should return original after encode then decode', () => { const original = '1640 Riverside Drive\nSuite $500\nPath\\to\\place'; expect(decodePostalAddress(encodePostalAddress(original))).toBe(original); }); it('should handle complex addresses', () => { const original = 'Acme Corp \\ Inc.\nBuilding $1, Floor 5\n1640 Riverside Drive\nHill Valley, CA 91103\nUSA'; expect(decodePostalAddress(encodePostalAddress(original))).toBe(original); }); }); }); ldapts-ldapts-91d5f4e/tests/ber/000077500000000000000000000000001522446626000166325ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/ber/BerReader.test.ts000066400000000000000000000260051522446626000220160ustar00rootroot00000000000000/* eslint-disable no-useless-concat */ import { describe, expect, it } from 'vite-plus/test'; import { Ber, BerReader } from '../../src/ber/index.js'; describe('BerReader', () => { describe('constructor', () => { it('should throw TypeError when no data provided', () => { expect(() => new BerReader(undefined as unknown as Buffer)).toThrow('data must be a Buffer'); }); it('should accept a Buffer', () => { const reader = new BerReader(Buffer.from([0x01])); expect(reader).toBeInstanceOf(BerReader); }); }); describe('#readByte()', () => { it('should read a single byte', () => { const reader = new BerReader(Buffer.from([0xde])); expect(reader.readByte()!).toBe(0xde); }); it('should return null when no data remains', () => { const reader = new BerReader(Buffer.from([])); expect(reader.readByte()).toBeNull(); }); it('should not advance offset when peeking', () => { const reader = new BerReader(Buffer.from([0xde, 0xad])); expect(reader.readByte(true)!).toBe(0xde); expect(reader.readByte(true)!).toBe(0xde); expect(reader.offset).toBe(0); }); }); describe('#peek()', () => { it('should return next byte without advancing', () => { const reader = new BerReader(Buffer.from([0xde, 0xad])); expect(reader.peek()!).toBe(0xde); expect(reader.peek()!).toBe(0xde); }); }); describe('#readInt()', () => { it('should read 1 byte integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x01, 0x03])); expect(reader.readInt()!).toBe(0x03); expect(reader.length).toBe(0x01); }); it('should read 2 byte integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x02, 0x7e, 0xde])); expect(reader.readInt()!).toBe(0x7ede); expect(reader.length).toBe(0x02); }); it('should read 3 byte integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x03, 0x7e, 0xde, 0x03])); expect(reader.readInt()!).toBe(0x7ede03); expect(reader.length).toBe(0x03); }); it('should read 4 byte integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x04, 0x7e, 0xde, 0x03, 0x01])); expect(reader.readInt()!).toBe(0x7ede0301); expect(reader.length).toBe(0x04); }); it('should read 1 byte negative integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x01, 0xdc])); expect(reader.readInt()!).toBe(-36); expect(reader.length).toBe(0x01); }); it('should read 2 byte negative integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x02, 0xc0, 0x4e])); expect(reader.readInt()!).toBe(-16306); expect(reader.length).toBe(0x02); }); it('should read 3 byte negative integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x03, 0xff, 0x00, 0x19])); expect(reader.readInt()!).toBe(-65511); expect(reader.length).toBe(0x03); }); it('should read 4 byte negative integer', () => { const reader = new BerReader(Buffer.from([0x02, 0x04, 0x91, 0x7c, 0x22, 0x1f])); expect(reader.readInt()!).toBe(-1854135777); expect(reader.length).toBe(0x04); }); it('should throw on wrong tag', () => { const reader = new BerReader(Buffer.from([0x04, 0x01, 0x03])); expect(() => reader.readInt()).toThrow('Expected 0x2: got 0x4'); }); }); describe('#readBoolean()', () => { it('should read true', () => { const reader = new BerReader(Buffer.from([0x01, 0x01, 0xff])); expect(reader.readBoolean()!).toBe(true); expect(reader.length).toBe(0x01); }); it('should read false', () => { const reader = new BerReader(Buffer.from([0x01, 0x01, 0x00])); expect(reader.readBoolean()!).toBe(false); expect(reader.length).toBe(0x01); }); }); describe('#readEnumeration()', () => { it('should read enumeration value', () => { const reader = new BerReader(Buffer.from([0x0a, 0x01, 0x20])); expect(reader.readEnumeration()!).toBe(0x20); expect(reader.length).toBe(0x01); }); }); describe('#readString()', () => { it('should read string', () => { const dn = 'cn=foo,ou=unit,o=test'; const buf = Buffer.alloc(dn.length + 2); buf[0] = 0x04; buf[1] = Buffer.byteLength(dn); buf.write(dn, 2); const reader = new BerReader(buf); expect(reader.readString()!).toBe(dn); expect(reader.length).toBe(dn.length); }); it('should read empty string', () => { const reader = new BerReader(Buffer.from([0x04, 0x00])); expect(reader.readString()!).toBe(''); expect(reader.length).toBe(0); }); it('should read string as buffer', () => { const reader = new BerReader(Buffer.from([0x04, 0x03, 0x66, 0x6f, 0x6f])); const result = reader.readString(Ber.OctetString, true)!; expect(Buffer.isBuffer(result)).toBe(true); expect(result.toString()).toBe('foo'); }); it('should read long string (extended length encoding)', () => { const longString = '2;649;CN=Red Hat CS 71GA Demo,O=Red Hat CS 71GA Demo,C=US;' + 'CN=RHCS Agent - admin01,UID=admin01,O=redhat,C=US [1] This is ' + 'a comment. [2] Some Role in the CS'; const buf = Buffer.alloc(3 + longString.length); buf[0] = 0x04; buf[1] = 0x81; buf[2] = longString.length; buf.write(longString, 3); const reader = new BerReader(buf); expect(reader.readString()!).toBe(longString); expect(reader.length).toBe(longString.length); }); it('should throw on wrong tag', () => { const reader = new BerReader(Buffer.from([0x02, 0x01, 0x03])); expect(() => reader.readString()).toThrow('Expected 0x4: got 0x2'); }); }); describe('#readSequence()', () => { it('should read sequence', () => { const reader = new BerReader(Buffer.from([0x30, 0x03, 0x01, 0x01, 0xff])); expect(reader.readSequence()!).toBe(0x30); expect(reader.length).toBe(0x03); expect(reader.readBoolean()!).toBe(true); expect(reader.length).toBe(0x01); }); it('should validate expected tag', () => { const reader = new BerReader(Buffer.from([0x30, 0x03, 0x01, 0x01, 0xff])); expect(() => reader.readSequence(0x31)).toThrow('Expected 0x31: got 0x30'); }); }); describe('#readOID()', () => { it('should read OID', () => { const reader = new BerReader(Buffer.from([0x06, 0x09, 0x2a, 0x86, 0x48, 0x86, 0xf7, 0x0d, 0x01, 0x01, 0x01])); expect(reader.readOID()!).toBe('1.2.840.113549.1.1.1'); }); }); describe('LDAP message parsing', () => { it('should parse anonymous LDAPv3 bind request', () => { const bindRequest = Buffer.from([0x30, 12, 0x02, 1, 0x04, 0x60, 7, 0x02, 1, 0x03, 0x04, 0, 0x80, 0]); const reader = new BerReader(bindRequest); expect(reader.readSequence()!).toBe(0x30); expect(reader.length).toBe(12); expect(reader.readInt()!).toBe(4); expect(reader.readSequence()!).toBe(0x60); expect(reader.length).toBe(7); expect(reader.readInt()!).toBe(3); expect(reader.readString()!).toBe(''); expect(reader.length).toBe(0); expect(reader.readByte()!).toBe(0x80); expect(reader.readByte()!).toBe(0); expect(reader.readByte()).toBeNull(); }); }); describe('buffer properties', () => { it('should track offset correctly', () => { const reader = new BerReader(Buffer.from([0x01, 0x02, 0x03, 0x04])); expect(reader.offset).toBe(0); reader.readByte(); expect(reader.offset).toBe(1); reader.readByte(); expect(reader.offset).toBe(2); }); it('should track remaining bytes', () => { const reader = new BerReader(Buffer.from([0x01, 0x02, 0x03, 0x04])); expect(reader.remain).toBe(4); reader.readByte(); expect(reader.remain).toBe(3); }); it('should return remaining buffer', () => { const reader = new BerReader(Buffer.from([0x01, 0x02, 0x03, 0x04])); reader.readByte(); reader.readByte(); expect(reader.remainingBuffer).toStrictEqual(Buffer.from([0x03, 0x04])); }); }); describe('issue #151 - buffer misalignment', () => { it('should handle multiple LDAP messages in single buffer', () => { const message1 = Buffer.from([0x30, 0x06, 0x02, 0x01, 0x01, 0x04, 0x01, 0x41]); const message2 = Buffer.from([0x30, 0x06, 0x02, 0x01, 0x02, 0x04, 0x01, 0x42]); const combined = Buffer.concat([message1, message2]); const reader1 = new BerReader(combined); expect(reader1.readSequence()!).toBe(0x30); expect(reader1.readInt()!).toBe(1); expect(reader1.readString()!).toBe('A'); const remaining = combined.subarray(reader1.offset); const reader2 = new BerReader(remaining); expect(reader2.readSequence()!).toBe(0x30); expect(reader2.readInt()!).toBe(2); expect(reader2.readString()!).toBe('B'); }); it('should handle setBufferSize for partial reads', () => { const message = Buffer.from([0x30, 0x06, 0x02, 0x01, 0x01, 0x04, 0x01, 0x41, 0xff, 0xff]); const reader = new BerReader(message); reader.setBufferSize(8); expect(reader.readSequence()!).toBe(0x30); expect(reader.readInt()!).toBe(1); expect(reader.readString()!).toBe('A'); expect(reader.remain).toBe(0); }); it('should correctly calculate remain after setBufferSize', () => { const buffer = Buffer.from([0x01, 0x02, 0x03, 0x04, 0x05, 0x06]); const reader = new BerReader(buffer); expect(reader.remain).toBe(6); reader.setBufferSize(4); expect(reader.remain).toBe(4); reader.readByte(); expect(reader.remain).toBe(3); }); }); describe('length encoding', () => { it('should handle short form length (0-127)', () => { const reader = new BerReader(Buffer.from([0x04, 0x05, 0x68, 0x65, 0x6c, 0x6c, 0x6f])); expect(reader.readString()!).toBe('hello'); }); it('should handle long form length with 1 byte', () => { const data = Buffer.alloc(131); data[0] = 0x04; data[1] = 0x81; data[2] = 128; data.fill(0x41, 3); const reader = new BerReader(data); const result = reader.readString()!; expect(result.length).toBe(128); expect(result).toBe('A'.repeat(128)); }); it('should handle long form length with 2 bytes', () => { const data = Buffer.alloc(260); data[0] = 0x04; data[1] = 0x82; data[2] = 0x01; data[3] = 0x00; data.fill(0x42, 4); const reader = new BerReader(data); const result = reader.readString()!; expect(result.length).toBe(256); expect(result).toBe('B'.repeat(256)); }); it('should throw on indefinite length', () => { const reader = new BerReader(Buffer.from([0x30, 0x80])); expect(() => reader.readSequence()).toThrow('Indefinite length not supported'); }); it('should throw on encoding too long', () => { const reader = new BerReader(Buffer.from([0x30, 0x85, 0x01, 0x02, 0x03, 0x04, 0x05])); expect(() => reader.readSequence()).toThrow('Encoding too long'); }); }); }); ldapts-ldapts-91d5f4e/tests/ber/BerWriter.test.ts000066400000000000000000000316001522446626000220650ustar00rootroot00000000000000/* eslint-disable no-bitwise */ import { describe, expect, it } from 'vite-plus/test'; import { Ber, BerReader, BerWriter } from '../../src/ber/index.js'; describe('BerWriter', () => { describe('constructor', () => { it('should create with default options', () => { const writer = new BerWriter(); expect(writer).toBeInstanceOf(BerWriter); }); it('should accept custom size option', () => { const writer = new BerWriter({ size: 512 }); expect(writer).toBeInstanceOf(BerWriter); }); }); describe('#buffer', () => { it('should throw if sequences are not ended', () => { const writer = new BerWriter(); writer.startSequence(); expect(() => writer.buffer).toThrow('1 unended sequence(s)'); }); }); describe('#writeByte()', () => { it('should write a single byte', () => { const writer = new BerWriter(); writer.writeByte(0xc2); const buffer = writer.buffer; expect(buffer.length).toBe(1); expect(buffer[0]!).toBe(0xc2); }); }); describe('#writeInt()', () => { it('should write 1 byte integer', () => { const writer = new BerWriter(); writer.writeInt(0x7f); const buffer = writer.buffer; expect(buffer.length).toBe(3); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x01); expect(buffer[2]!).toBe(0x7f); }); it('should write 2 byte integer', () => { const writer = new BerWriter(); writer.writeInt(0x7ffe); const buffer = writer.buffer; expect(buffer.length).toBe(4); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x02); expect(buffer[2]!).toBe(0x7f); expect(buffer[3]!).toBe(0xfe); }); it('should write 3 byte integer', () => { const writer = new BerWriter(); writer.writeInt(0x7ffffe); const buffer = writer.buffer; expect(buffer.length).toBe(5); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x03); expect(buffer[2]!).toBe(0x7f); expect(buffer[3]!).toBe(0xff); expect(buffer[4]!).toBe(0xfe); }); it('should write 4 byte integer', () => { const writer = new BerWriter(); writer.writeInt(0x7ffffffe); const buffer = writer.buffer; expect(buffer.length).toBe(6); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x04); expect(buffer[2]!).toBe(0x7f); expect(buffer[3]!).toBe(0xff); expect(buffer[4]!).toBe(0xff); expect(buffer[5]!).toBe(0xfe); }); it('should write 1 byte negative integer', () => { const writer = new BerWriter(); writer.writeInt(-128); const buffer = writer.buffer; expect(buffer.length).toBe(3); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x01); expect(buffer[2]!).toBe(0x80); }); it('should write 2 byte negative integer', () => { const writer = new BerWriter(); writer.writeInt(-22400); const buffer = writer.buffer; expect(buffer.length).toBe(4); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x02); expect(buffer[2]!).toBe(0xa8); expect(buffer[3]!).toBe(0x80); }); it('should write 3 byte negative integer', () => { const writer = new BerWriter(); writer.writeInt(-481653); const buffer = writer.buffer; expect(buffer.length).toBe(5); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x03); expect(buffer[2]!).toBe(0xf8); expect(buffer[3]!).toBe(0xa6); expect(buffer[4]!).toBe(0x8b); }); it('should write 4 byte negative integer', () => { const writer = new BerWriter(); writer.writeInt(-1522904131); const buffer = writer.buffer; expect(buffer.length).toBe(6); expect(buffer[0]!).toBe(Ber.Integer); expect(buffer[1]!).toBe(0x04); expect(buffer[2]!).toBe(0xa5); expect(buffer[3]!).toBe(0x3a); expect(buffer[4]!).toBe(0x53); expect(buffer[5]!).toBe(0xbd); }); }); describe('#writeBoolean()', () => { it('should write true', () => { const writer = new BerWriter(); writer.writeBoolean(true); const buffer = writer.buffer; expect(buffer.length).toBe(3); expect(buffer[0]!).toBe(Ber.Boolean); expect(buffer[1]!).toBe(0x01); expect(buffer[2]!).toBe(0xff); }); it('should write false', () => { const writer = new BerWriter(); writer.writeBoolean(false); const buffer = writer.buffer; expect(buffer.length).toBe(3); expect(buffer[0]!).toBe(Ber.Boolean); expect(buffer[1]!).toBe(0x01); expect(buffer[2]!).toBe(0x00); }); }); describe('#writeNull()', () => { it('should write null', () => { const writer = new BerWriter(); writer.writeNull(); const buffer = writer.buffer; expect(buffer.length).toBe(2); expect(buffer[0]!).toBe(Ber.Null); expect(buffer[1]!).toBe(0x00); }); }); describe('#writeEnumeration()', () => { it('should write enumeration', () => { const writer = new BerWriter(); writer.writeEnumeration(0x20); const buffer = writer.buffer; expect(buffer.length).toBe(3); expect(buffer[0]!).toBe(Ber.Enumeration); expect(buffer[1]!).toBe(0x01); expect(buffer[2]!).toBe(0x20); }); }); describe('#writeString()', () => { it('should write string', () => { const writer = new BerWriter(); writer.writeString('hello world'); const buffer = writer.buffer; expect(buffer.length).toBe(13); expect(buffer[0]!).toBe(Ber.OctetString); expect(buffer[1]!).toBe(11); expect(buffer.subarray(2).toString()).toBe('hello world'); }); it('should write empty string', () => { const writer = new BerWriter(); writer.writeString(''); const buffer = writer.buffer; expect(buffer.length).toBe(2); expect(buffer[0]!).toBe(Ber.OctetString); expect(buffer[1]!).toBe(0); }); it('should write string with custom tag', () => { const writer = new BerWriter(); writer.writeString('test', 0x80); const buffer = writer.buffer; expect(buffer[0]!).toBe(0x80); }); it('should throw on non-string', () => { const writer = new BerWriter(); expect(() => writer.writeString(123 as unknown as string)).toThrow(TypeError); }); }); describe('#writeBuffer()', () => { it('should write buffer with tag', () => { const writer = new BerWriter(); writer.writeBuffer(Buffer.from([0x04, 0x05, 0x06]), 0x04); const buffer = writer.buffer; expect(buffer.length).toBe(5); expect(buffer[0]!).toBe(0x04); expect(buffer[1]!).toBe(0x03); expect(buffer.subarray(2)).toStrictEqual(Buffer.from([0x04, 0x05, 0x06])); }); it('should throw on non-buffer', () => { const writer = new BerWriter(); expect(() => writer.writeBuffer('test' as unknown as Buffer, 0x04)).toThrow(TypeError); }); }); describe('#writeStringArray()', () => { it('should write array of strings', () => { const writer = new BerWriter(); writer.writeStringArray(['hello', 'world']); const buffer = writer.buffer; const reader = new BerReader(buffer); expect(reader.readString()!).toBe('hello'); expect(reader.readString()!).toBe('world'); }); }); describe('#writeOID()', () => { it('should write OID', () => { const writer = new BerWriter(); writer.writeOID('1.2.840.113549.1.1.1'); const buffer = writer.buffer; const reader = new BerReader(buffer); expect(reader.readOID()!).toBe('1.2.840.113549.1.1.1'); }); it('should throw on invalid OID', () => { const writer = new BerWriter(); expect(() => writer.writeOID('1.2')).toThrow('not a valid OID'); }); }); describe('#startSequence() / #endSequence()', () => { it('should write simple sequence', () => { const writer = new BerWriter(); writer.startSequence(); writer.writeBoolean(true); writer.endSequence(); const buffer = writer.buffer; expect(buffer[0]!).toBe(Ber.Sequence | Ber.Constructor); const reader = new BerReader(buffer); expect(reader.readSequence()!).toBe(0x30); expect(reader.readBoolean()!).toBe(true); }); it('should write nested sequences', () => { const writer = new BerWriter(); writer.startSequence(); writer.startSequence(); writer.writeString('hello'); writer.endSequence(); writer.endSequence(); const buffer = writer.buffer; const reader = new BerReader(buffer); expect(reader.readSequence()!).toBe(0x30); expect(reader.readSequence()!).toBe(0x30); expect(reader.readString()!).toBe('hello'); }); it('should write sequence with custom tag', () => { const writer = new BerWriter(); writer.startSequence(0x60); writer.writeInt(3); writer.endSequence(); const buffer = writer.buffer; expect(buffer[0]!).toBe(0x60); }); it('should throw on endSequence without startSequence', () => { const writer = new BerWriter(); expect(() => writer.endSequence()).toThrow('No sequence to end'); }); }); describe('LDAP message writing', () => { it('should write anonymous bind request', () => { const writer = new BerWriter(); writer.startSequence(); writer.writeInt(1); writer.startSequence(0x60); writer.writeInt(3); writer.writeString(''); writer.writeByte(0x80); writer.writeByte(0x00); writer.endSequence(); writer.endSequence(); const buffer = writer.buffer; const reader = new BerReader(buffer); expect(reader.readSequence()!).toBe(0x30); expect(reader.readInt()!).toBe(1); expect(reader.readSequence()!).toBe(0x60); expect(reader.readInt()!).toBe(3); expect(reader.readString()!).toBe(''); expect(reader.readByte()!).toBe(0x80); expect(reader.readByte()!).toBe(0x00); }); }); describe('buffer growth', () => { it('should automatically resize buffer when needed', () => { const writer = new BerWriter({ size: 8 }); writer.writeString('this is a longer string that exceeds initial buffer'); const buffer = writer.buffer; expect(buffer.length).toBeGreaterThan(8); }); }); describe('length encoding', () => { it('should use short form for length <= 127', () => { const writer = new BerWriter(); writer.writeString('a'.repeat(127)); const buffer = writer.buffer; expect(buffer[1]!).toBe(127); }); it('should use long form with 1 byte for length 128-255', () => { const writer = new BerWriter(); writer.writeString('a'.repeat(128)); const buffer = writer.buffer; expect(buffer[1]!).toBe(0x81); expect(buffer[2]!).toBe(128); }); it('should use long form with 2 bytes for length 256-65535', () => { const writer = new BerWriter(); writer.writeString('a'.repeat(256)); const buffer = writer.buffer; expect(buffer[1]!).toBe(0x82); expect(buffer[2]!).toBe(0x01); expect(buffer[3]!).toBe(0x00); }); }); describe('round-trip tests', () => { it('should round-trip integers', () => { const values = [0, 1, 127, 128, 255, 256, 65535, 65536, -1, -128, -129, -32768]; for (const value of values) { const writer = new BerWriter(); writer.writeInt(value); const reader = new BerReader(writer.buffer); expect(reader.readInt()!).toBe(value); } }); it('should round-trip strings', () => { const strings = ['', 'hello', 'a'.repeat(127), 'b'.repeat(128), 'c'.repeat(256)]; for (const str of strings) { const writer = new BerWriter(); writer.writeString(str); const reader = new BerReader(writer.buffer); expect(reader.readString()!).toBe(str); } }); it('should round-trip complex LDAP structure', () => { const writer = new BerWriter(); writer.startSequence(); writer.writeInt(42); writer.startSequence(0x63); writer.writeString('dc=example,dc=com'); writer.writeEnumeration(2); writer.writeEnumeration(0); writer.writeInt(0); writer.writeInt(0); writer.writeBoolean(false); writer.writeString('(objectClass=*)'); writer.endSequence(); writer.endSequence(); const buffer = writer.buffer; const reader = new BerReader(buffer); expect(reader.readSequence()!).toBe(0x30); expect(reader.readInt()!).toBe(42); expect(reader.readSequence()!).toBe(0x63); expect(reader.readString()!).toBe('dc=example,dc=com'); expect(reader.readEnumeration()!).toBe(2); expect(reader.readEnumeration()!).toBe(0); expect(reader.readInt()!).toBe(0); expect(reader.readInt()!).toBe(0); expect(reader.readBoolean()!).toBe(false); expect(reader.readString()!).toBe('(objectClass=*)'); }); }); }); ldapts-ldapts-91d5f4e/tests/data/000077500000000000000000000000001522446626000167735ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/data/generate-certs.mjs000066400000000000000000000072421522446626000224230ustar00rootroot00000000000000import { promises as fs } from 'node:fs'; import path from 'node:path'; import { fileURLToPath } from 'node:url'; import forge from 'node-forge'; // Get script directory (ESM equivalent of __dirname) // eslint-disable-next-line no-redeclare const __filename = fileURLToPath(import.meta.url); const scriptDir = path.dirname(__filename); const certsDir = path.join(scriptDir, 'certs'); // Create certs directory if it doesn't exist await fs.mkdir(certsDir, { recursive: true }); function generateKeyPair() { return forge.pki.rsa.generateKeyPair({ bits: 2048 }); } async function writePem(filename, pem) { // eslint-disable-next-line security/detect-non-literal-fs-filename await fs.writeFile(path.join(certsDir, filename), pem.replace(/\r\n/g, '\n')); // Force LF if (filename.endsWith('-key.pem')) { try { // eslint-disable-next-line security/detect-non-literal-fs-filename await fs.chmod(path.join(certsDir, filename), '600'); } catch { // Ignore on Windows } } } // Generate CA const caKeys = generateKeyPair(); const caCert = forge.pki.createCertificate(); caCert.publicKey = caKeys.publicKey; caCert.serialNumber = '01'; caCert.validity.notBefore = new Date(); caCert.validity.notAfter = new Date(caCert.validity.notBefore.getTime() + 730 * 24 * 60 * 60 * 1000); // 2 years const caAttrs = [{ name: 'commonName', value: 'My Root CA' }]; caCert.setSubject(caAttrs); caCert.setIssuer(caAttrs); caCert.setExtensions([{ name: 'basicConstraints', cA: true, critical: true }]); caCert.sign(caKeys.privateKey, forge.md.sha256.create()); // Generate server certificate const serverKeys = generateKeyPair(); const serverCert = forge.pki.createCertificate(); serverCert.publicKey = serverKeys.publicKey; serverCert.serialNumber = '02'; serverCert.validity.notBefore = new Date(); serverCert.validity.notAfter = new Date(serverCert.validity.notBefore.getTime() + 365 * 24 * 60 * 60 * 1000); // 1 year serverCert.setSubject([{ name: 'commonName', value: 'ldap.local' }]); serverCert.setIssuer(caAttrs); serverCert.setExtensions([ { name: 'subjectAltName', altNames: [ { type: 2, value: 'ldap.local' }, // type 2 is DNSName { type: 2, value: 'localhost' }, ], }, { name: 'keyUsage', digitalSignature: true, keyEncipherment: true, critical: true }, { name: 'extKeyUsage', serverAuth: true, critical: true }, { name: 'basicConstraints', cA: false }, ]); serverCert.sign(caKeys.privateKey, forge.md.sha256.create()); // Generate client certificate const clientKeys = generateKeyPair(); const clientCert = forge.pki.createCertificate(); clientCert.publicKey = clientKeys.publicKey; clientCert.serialNumber = '03'; clientCert.validity.notBefore = new Date(); clientCert.validity.notAfter = new Date(clientCert.validity.notBefore.getTime() + 365 * 24 * 60 * 60 * 1000); // 1 year clientCert.setSubject([{ name: 'commonName', value: 'ldap-client' }]); clientCert.setIssuer(caAttrs); clientCert.setExtensions([ { name: 'keyUsage', digitalSignature: true, critical: true }, { name: 'extKeyUsage', clientAuth: true, critical: true }, { name: 'basicConstraints', cA: false }, ]); clientCert.sign(caKeys.privateKey, forge.md.sha256.create()); // Write files await Promise.all([ writePem('ca.pem', forge.pki.certificateToPem(caCert)), writePem('server.pem', forge.pki.certificateToPem(serverCert)), writePem('server-key.pem', forge.pki.privateKeyToPem(serverKeys.privateKey)), writePem('client.pem', forge.pki.certificateToPem(clientCert)), writePem('client-key.pem', forge.pki.privateKeyToPem(clientKeys.privateKey)), ]); // eslint-disable-next-line no-console console.log(`Generated ca.pem, server.pem, server-key.pem, client.pem, and client-key.pem in ${certsDir}`); ldapts-ldapts-91d5f4e/tests/data/ldif/000077500000000000000000000000001522446626000177115ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/data/ldif/00-sssvlv-module.ldif000066400000000000000000000004461522446626000236150ustar00rootroot00000000000000dn: cn=module{0},cn=config changeType: modify add: olcModuleLoad olcModuleLoad: {0}sssvlv dn: olcOverlay={0}sssvlv,olcDatabase={1}{{ LDAP_BACKEND }},cn=config changeType: add objectClass: olcOverlayConfig objectClass: olcSssVlvConfig olcOverlay: {0}sssvlv olcSssVlvMax: 8 olcSssVlvMaxKeys: 5 ldapts-ldapts-91d5f4e/tests/data/ldif/02-ppolicy-module.ldif000066400000000000000000000006661522446626000237420ustar00rootroot00000000000000#load password policy module dn: cn=module{0},cn=config changetype: modify add: olcModuleLoad olcModuleLoad: {0}ppolicy #configure password policy module dn: olcOverlay=ppolicy,olcDatabase={1}{{ LDAP_BACKEND }},cn=config changetype: add objectClass: olcPPolicyConfig objectClass: olcOverlayConfig olcOverlay: ppolicy olcPPolicyDefault: cn=default,ou=pwpolicies,{{ LDAP_BASE_DN }} olcPPolicyHashCleartext: TRUE olcPPolicyUseLockout: TRUE ldapts-ldapts-91d5f4e/tests/data/ldif/03-pwpolicy-enable.ldif000066400000000000000000000010101522446626000240530ustar00rootroot00000000000000#load password policy module dn: ou=pwpolicies,{{ LDAP_BASE_DN }} objectClass: organizationalUnit objectClass: top ou: pwpolicies dn: cn=default,ou=pwpolicies,{{ LDAP_BASE_DN }} objectClass: top objectClass: device objectClass: pwdPolicy cn: default pwdAttribute: userPassword pwdAllowUserChange: TRUE pwdCheckQuality: 1 pwdExpireWarning: 3600 pwdFailureCountInterval: 3600 pwdInHistory: 3 pwdLockout: TRUE pwdLockoutDuration: 300 pwdMaxAge: 0 pwdMaxFailure: 5 pwdMinLength: 6 pwdMustChange: FALSE pwdSafeModify: FALSE ldapts-ldapts-91d5f4e/tests/data/ldif/10-users.ldif000066400000000000000000000051651522446626000221370ustar00rootroot00000000000000dn: uid=user,{{ LDAP_BASE_DN }} uid: user cn: user sn: 3 objectClass: top objectClass: posixAccount objectClass: inetOrgPerson loginShell: /bin/bash homeDirectory: /home/user uidNumber: 14583100 gidNumber: 14564100 mail: user@ldap.local userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=user1,{{ LDAP_BASE_DN }} uid: user1 cn: user1 sn: SURNAME objectClass: top objectClass: posixAccount objectClass: inetOrgPerson loginShell: /bin/bash homeDirectory: /home/user uidNumber: 14583101 gidNumber: 14564100 mail: user1@ldap.local userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=user2,{{ LDAP_BASE_DN }} uid: user2 cn: user2 sn: SURNAME objectClass: top objectClass: posixAccount objectClass: inetOrgPerson loginShell: /bin/bash homeDirectory: /home/user uidNumber: 14583102 gidNumber: 14564100 mail: user2@ldap.local userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=user3,{{ LDAP_BASE_DN }} uid: user3 cn: user3 sn: SURNAME objectClass: top objectClass: posixAccount objectClass: inetOrgPerson loginShell: /bin/bash homeDirectory: /home/user uidNumber: 14583103 gidNumber: 14564100 mail: user3@ldap.local userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=user4,{{ LDAP_BASE_DN }} uid: user4 cn: user4 sn: SURNAME objectClass: top objectClass: posixAccount objectClass: inetOrgPerson loginShell: /bin/bash homeDirectory: /home/user uidNumber: 14583104 gidNumber: 14564100 mail: user4@ldap.local userPassword: {SSHA}UpZ0zp8NRV/2AmED0TnDtpaEhjLQyXA9 dn: ou=People,{{ LDAP_BASE_DN }} objectClass: top objectClass: organizationalUnit ou: People dn: cn=UserGroup1,{{ LDAP_BASE_DN }} objectClass: posixGroup objectClass: top cn: UserGroup1 gidNumber: 49701 memberUid: user3 memberUid: user4 dn: uid=person1,ou=People,{{ LDAP_BASE_DN }} objectClass: posixAccount objectClass: top objectClass: inetOrgPerson gidNumber: 0 givenName: person1 uid: person1 sn: SURNAME homeDirectory: /person1 cn: person1 uidNumber: 59547 userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=person2,ou=People,{{ LDAP_BASE_DN }} objectClass: posixAccount objectClass: top objectClass: inetOrgPerson gidNumber: 0 givenName: person2 sn: SURNAME uid: person2 homeDirectory: /person2 cn: person2 uidNumber: 60743 userPassword: {SHA}cRDtpNCeBiql5KOQsKVyrA0sAiA= dn: uid=asd,ou=pwpolicies,{{ LDAP_BASE_DN }} objectClass: posixAccount objectClass: top objectClass: inetOrgPerson gidNumber: 0 givenName: asd uid: asd homeDirectory: asd sn: asd cn: asd uidNumber: 7108 userPassword: {SSHA}3fd6xNG4GN5hsPhWmR8CT4JOJOGB7DNh dn: cn=UserGroup2 (Test),{{ LDAP_BASE_DN }} objectClass: posixGroup objectClass: top cn: UserGroup2 (Test) memberUid: user3 memberUid: user2 gidNumber: 2539 ldapts-ldapts-91d5f4e/tests/data/ldif/11-userpw.ldif000066400000000000000000000002221522446626000223110ustar00rootroot00000000000000dn: uid=user4,{{ LDAP_BASE_DN }} changetype: modify add: pwdPolicySubentry pwdPolicySubentry: cn=passwordSpecial,ou=pwpolicies,{{ LDAP_BASE_DN }} ldapts-ldapts-91d5f4e/tests/dn/000077500000000000000000000000001522446626000164635ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/dn/DN.test.ts000066400000000000000000000037241522446626000203200ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { DN } from '../../src/index.js'; describe('DN', () => { describe('#toString()', () => { it('should chain build & escape correctly', () => { const dn = new DN() .addRDN({ cn: 'Smith, James K.', oa: 'eu', }) .addPairRDN('dc', 'domain') .addRDNs({ dc: 'gb', group: 'all', }); expect(dn.toString()).toBe('cn=Smith\\, James K.+oa=eu,dc=domain,dc=gb,group=all'); }); it('should create a correct DN from object', () => { const dn = new DN({ cn: 'Smith, James K.', oa: 'eu', dc: 'domain', }).addRDNs({ dc: 'gb', group: 'all', }); expect(dn.toString()).toBe('cn=Smith\\, James K.,oa=eu,dc=domain,dc=gb,group=all'); }); it('should handle values with the same key', () => { const dn = new DN({ oa: 'eu', dc: ['domain1', 'domain2'], }); expect(dn.toString()).toBe('oa=eu,dc=domain1,dc=domain2'); }); }); describe('#equals()', () => { it('should equal two exact objects', () => { const dn1 = new DN({ dc: 'domain', oa: 'eu' }); const dn2 = new DN({ dc: 'domain', oa: 'eu' }); expect(dn1.equals(dn2)).toBe(true); }); it('should not equal two different objects', () => { const dn1 = new DN({ oa: 'eu', dc: 'domain' }); const dn2 = new DN({ dc: 'domain', oa: 'eu' }); expect(dn1.equals(dn2)).not.toBe(true); }); }); describe('#clone()', () => { it('should clone when RDNs have a value', () => { const dn = new DN({ dc: ['hello'], oa: 'aaa' }); const clone1 = dn.clone().addPairRDN('cn', 'test'); const clone2 = dn.clone().addPairRDN('cn', 'test'); expect(dn.toString()).toBe('dc=hello,oa=aaa'); expect(clone1.toString()).toBe('dc=hello,oa=aaa,cn=test'); expect(clone2.toString()).toBe('dc=hello,oa=aaa,cn=test'); }); }); }); ldapts-ldapts-91d5f4e/tests/dn/RDN.test.ts000066400000000000000000000035721522446626000204430ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { RDN } from '../../src/dn/RDN.js'; describe('RDN', () => { describe('#toString()', () => { it('should format & escape correct string representation', () => { const rdn1 = new RDN({ cn: 'Smith, James K.' }); expect(rdn1.toString()).toBe('cn=Smith\\, James K.'); const rdn2 = new RDN({ ou: 'Sales\\Engineering' }); expect(rdn2.toString()).toBe('ou=Sales\\\\Engineering'); const rdn3 = new RDN({ cn: 'East#Test + Lab' }); expect(rdn3.toString()).toBe('cn=East\\#Test \\+ Lab'); const rdn4 = new RDN({ cn: ' Jim Smith ' }); expect(rdn4.toString()).toBe('cn=" Jim Smith "'); const rdn5 = new RDN({ cn: ' Bob Barker' }); expect(rdn5.toString()).toBe('cn=" Bob Barker"'); }); it('should format correct compound string representation', () => { const rdn = new RDN({ dc: 'domain', oa: 'eu' }); expect(rdn.toString()).toBe('dc=domain+oa=eu'); }); }); describe('#equals()', () => { it('should equal two exact objects', () => { const rdn1 = new RDN({ dc: 'domain', oa: 'eu' }); const rdn2 = new RDN({ dc: 'domain', oa: 'eu' }); expect(rdn1.equals(rdn2)).toBe(true); const rdn3 = new RDN({ oa: 'eu', dc: 'domain' }); const rdn4 = new RDN({ dc: 'domain', oa: 'eu' }); expect(rdn3.equals(rdn4)).toBe(true); }); it('should not equal two different objects', () => { const rdn1 = new RDN({ dc: 'domain1' }); const rdn2 = new RDN({ dc: 'domain2' }); expect(rdn1.equals(rdn2)).not.toBe(true); const rdn3 = new RDN({ dc: 'same' }); const rdn4 = new RDN({ oa: 'same' }); expect(rdn3.equals(rdn4)).not.toBe(true); const rdn5 = new RDN({ dc: 'domain', oa: 'eu' }); const rdn6 = new RDN({ oa: 'eu' }); expect(rdn5.equals(rdn6)).not.toBe(true); }); }); }); ldapts-ldapts-91d5f4e/tests/filters/000077500000000000000000000000001522446626000175325ustar00rootroot00000000000000ldapts-ldapts-91d5f4e/tests/filters/EqualityFilter.test.ts000066400000000000000000000032201522446626000240200ustar00rootroot00000000000000import { describe, expect, it, vi } from 'vite-plus/test'; import { type BerWriter } from '../../src/ber/index.js'; import { EqualityFilter } from '../../src/index.js'; describe('EqualityFilter', () => { describe('#writeFilter()', () => { it('should write: (o=Parens R Us (for all your parenthetical needs))', () => { const filter = new EqualityFilter({ attribute: 'o', value: 'Parens R Us (for all your parenthetical needs)', }); const berWriter = { writeString: vi.fn<(value: string) => void>() } as unknown as BerWriter; filter.writeFilter(berWriter); /* eslint-disable @typescript-eslint/unbound-method */ expect(berWriter.writeString).toHaveBeenCalledTimes(2); expect(berWriter.writeString).toHaveBeenNthCalledWith(1, 'o'); expect(berWriter.writeString).toHaveBeenNthCalledWith(2, 'Parens R Us (for all your parenthetical needs)'); /* eslint-enable @typescript-eslint/unbound-method */ }); it('should write: (displayName=My group (something))', () => { const filter = new EqualityFilter({ attribute: 'displayName', value: 'My group (something)', }); const berWriter = { writeString: vi.fn<(value: string) => void>() } as unknown as BerWriter; filter.writeFilter(berWriter); /* eslint-disable @typescript-eslint/unbound-method */ expect(berWriter.writeString).toHaveBeenCalledTimes(2); expect(berWriter.writeString).toHaveBeenNthCalledWith(1, 'displayName'); expect(berWriter.writeString).toHaveBeenNthCalledWith(2, 'My group (something)'); /* eslint-enable @typescript-eslint/unbound-method */ }); }); }); ldapts-ldapts-91d5f4e/tests/filters/ExtensibleFilter.test.ts000066400000000000000000000022761522446626000243370ustar00rootroot00000000000000import { describe, expect, it, vi } from 'vite-plus/test'; import { type BerWriter } from '../../src/ber/index.js'; import { ExtensibleFilter } from '../../src/index.js'; describe('ExtensibleFilter', () => { describe('#writeFilter()', () => { it('should write: userAccountControl:1.2.840.113556.1.4.803:=2', () => { const filter = new ExtensibleFilter({ matchType: 'userAccountControl', rule: '1.2.840.113556.1.4.803', value: '2', }); const berWriter = { writeString: vi.fn<(value: string) => void>(), writeBoolean: vi.fn<(value: boolean) => void>(), } as unknown as BerWriter; filter.writeFilter(berWriter); /* eslint-disable @typescript-eslint/unbound-method */ expect(berWriter.writeString).toHaveBeenCalledTimes(3); expect(berWriter.writeBoolean).toHaveBeenCalledTimes(0); expect(berWriter.writeString).toHaveBeenNthCalledWith(1, '1.2.840.113556.1.4.803', 129); expect(berWriter.writeString).toHaveBeenNthCalledWith(2, 'userAccountControl', 130); expect(berWriter.writeString).toHaveBeenNthCalledWith(3, '2', 131); /* eslint-enable @typescript-eslint/unbound-method */ }); }); }); ldapts-ldapts-91d5f4e/tests/filters/Filter.test.ts000066400000000000000000000011751522446626000223110ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { Filter } from '../../src/index.js'; describe('Filter', () => { describe('#escape()', () => { it('should not touch plain letters', () => { expect(Filter.escape('x')).toBe('x'); }); it('should escape syntax characters', () => { expect(Filter.escape('*').toLowerCase()).toBe('\\2a'); expect(Filter.escape('(').toLowerCase()).toBe('\\28'); expect(Filter.escape(')').toLowerCase()).toBe('\\29'); expect(Filter.escape('\\').toLowerCase()).toBe('\\5c'); expect(Filter.escape('\0').toLowerCase()).toBe('\\00'); }); }); }); ldapts-ldapts-91d5f4e/tests/filters/NotFilter.test.ts000066400000000000000000000011661522446626000227720ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { EqualityFilter, Filter, NotFilter } from '../../src/index.js'; describe('NotFilter', () => { describe('#toString()', () => { it('should render sub filter toString value', () => { const displayNameFoo = new EqualityFilter({ attribute: 'displayName', value: 'Foo', }); const filter = new NotFilter({ filter: displayNameFoo, }); const fooName = `(${Filter.escape(displayNameFoo.attribute)}=${Filter.escape(displayNameFoo.value)})`; expect(filter.toString()).toBe(`(!${fooName})`); }); }); }); ldapts-ldapts-91d5f4e/tests/filters/OrFilter.test.ts000066400000000000000000000015621522446626000226120ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { EqualityFilter, Filter, OrFilter } from '../../src/index.js'; describe('OrFilter', () => { describe('#toString()', () => { it('should render sub filter toString values', () => { const displayNameFoo = new EqualityFilter({ attribute: 'displayName', value: 'Foo', }); const displayNameBar = new EqualityFilter({ attribute: 'displayName', value: 'Bar', }); const filter = new OrFilter({ filters: [displayNameFoo, displayNameBar], }); const fooName = `(${Filter.escape(displayNameFoo.attribute)}=${Filter.escape(displayNameFoo.value)})`; const barName = `(${Filter.escape(displayNameBar.attribute)}=${Filter.escape(displayNameBar.value)})`; expect(filter.toString()).toBe(`(|${fooName}${barName})`); }); }); }); ldapts-ldapts-91d5f4e/tests/filters/escapeFilter.test.ts000066400000000000000000000025571522446626000234770ustar00rootroot00000000000000import { describe, expect, it } from 'vite-plus/test'; import { escapeFilter } from '../../src/index.js'; describe('escapeFilter', () => { it('should escape filter syntax characters in interpolated values', () => { const value = '*)(uid=*'; expect(escapeFilter`(cn=${value})`).toBe('(cn=\\2a\\29\\28uid=\\2a)'); }); it('should not escape literal portions of the template', () => { expect(escapeFilter`(&(email=*@bar.com)(l=Seattle))`).toBe('(&(email=*@bar.com)(l=Seattle))'); }); it('should escape multiple interpolated values', () => { const mail = 'x*x@foo.net'; const organization = 'Parens (R Us)'; expect(escapeFilter`(&(email=${mail})(o=${organization}))`).toBe('(&(email=x\\2ax@foo.net)(o=Parens \\28R Us\\29))'); }); it('should stringify number and boolean values', () => { expect(escapeFilter`(&(uidNumber=${1000})(enabled=${true}))`).toBe('(&(uidNumber=1000)(enabled=true))'); }); it('should escape buffer values as hex escape sequences', () => { expect(escapeFilter`(objectGUID=${Buffer.from([0x01, 0xff])})`).toBe('(objectGUID=\\01\\ff)'); }); it('should support templates without interpolated values', () => { expect(escapeFilter`(cn=admin)`).toBe('(cn=admin)'); }); it('should support adjacent interpolated values', () => { expect(escapeFilter`(cn=${'a*'}${'(b'})`).toBe('(cn=a\\2a\\28b)'); }); }); ldapts-ldapts-91d5f4e/tests/import.tests.cjs000066400000000000000000000006771522446626000212500ustar00rootroot00000000000000'use strict'; /** * Test that the package can be imported from CommonJS * This catches issues like ESM-only dependencies breaking CJS builds */ const assert = require('node:assert'); const ldapts = require('../dist/index.cjs'); assert(ldapts.Client, 'Client should be exported'); assert(typeof ldapts.Client === 'function', 'Client should be a constructor'); // eslint-disable-next-line no-console console.log('✓ CJS import successful'); ldapts-ldapts-91d5f4e/tests/import.tests.mjs000066400000000000000000000005401522446626000212470ustar00rootroot00000000000000/** * Test that the package can be imported from ESM */ import assert from 'node:assert'; import * as ldapts from '../dist/index.mjs'; assert(ldapts.Client, 'Client should be exported'); assert(typeof ldapts.Client === 'function', 'Client should be a constructor'); // eslint-disable-next-line no-console console.log('✓ ESM import successful'); ldapts-ldapts-91d5f4e/tsconfig.build.json000066400000000000000000000001541522446626000205250ustar00rootroot00000000000000{ "extends": "./tsconfig.json", "compilerOptions": { "rootDir": "./src" }, "include": ["src"] } ldapts-ldapts-91d5f4e/tsconfig.json000066400000000000000000000013541522446626000174320ustar00rootroot00000000000000{ "compilerOptions": { "target": "ES2023", "module": "nodenext", "lib": ["ESNext"], "moduleResolution": "nodenext", "allowJs": false, "outDir": "dist", "rootDir": ".", "declaration": true, "isolatedDeclarations": true, "sourceMap": true, "sourceRoot": ".", "strict": true, "forceConsistentCasingInFileNames": true, "noUnusedLocals": true, "noUnusedParameters": true, "noImplicitOverride": true, "noImplicitReturns": true, "noFallthroughCasesInSwitch": true, "noPropertyAccessFromIndexSignature": true, "noUncheckedIndexedAccess": true, "types": ["node"] }, "include": ["src/**/*", "tests/**/*", "vite.config.ts"], "exclude": ["node_modules", "dist"] } ldapts-ldapts-91d5f4e/vite.config.ts000066400000000000000000000061121522446626000175030ustar00rootroot00000000000000import { oxlintConfig } from 'eslint-config-decent/oxlint'; import { type UserConfig } from 'vite'; import { defineConfig } from 'vite-plus'; type LintConfig = ReturnType; type LintRules = NonNullable; const baseLintConfig: LintConfig = oxlintConfig({ enableReact: false, enableTestingLibrary: false, enableVitest: true }); // These compat plugins import @typescript-eslint/typescript-estree, which cannot // load alongside typescript 7 (it supports typescript <6.1 only). Drop them and // their rules (member-ordering, explicit-member-accessibility, and a few vitest // padding/style rules) until typescript-eslint supports typescript 7. const estreeDependentPlugins = new Set(['@typescript-eslint/eslint-plugin', '@vitest/eslint-plugin']); const estreeDependentRulePrefixes = ['typescript-compat/', 'vitest-compat/']; function withoutEstreeDependentRules(rules: LintRules | undefined): LintRules { return Object.fromEntries(Object.entries(rules ?? {}).filter(([ruleName]) => !estreeDependentRulePrefixes.some((prefix) => ruleName.startsWith(prefix)))); } const config: UserConfig = defineConfig({ fmt: { printWidth: 200, singleQuote: true, }, lint: { ...baseLintConfig, jsPlugins: (baseLintConfig.jsPlugins ?? []).filter((plugin) => !estreeDependentPlugins.has(typeof plugin === 'string' ? plugin : plugin.specifier)), rules: { ...withoutEstreeDependentRules(baseLintConfig.rules), // Every switch in this codebase handles the remaining union members in a // default clause; treat that as exhaustive. 'typescript/switch-exhaustiveness-check': ['error', { considerDefaultExhaustiveForUnions: true }], }, overrides: [ ...(baseLintConfig.overrides ?? []) .map((override) => ({ ...override, rules: withoutEstreeDependentRules(override.rules), })) .filter((override) => Object.keys(override.rules).length > 0 || override.jsPlugins), { // Plain JavaScript helper scripts have no type annotations, so the // type-aware unsafe-* rules only produce noise for untyped imports. files: ['**/*.mjs', '**/*.cjs'], rules: { 'typescript/no-unsafe-argument': 'off', 'typescript/no-unsafe-assignment': 'off', 'typescript/no-unsafe-call': 'off', 'typescript/no-unsafe-member-access': 'off', 'typescript/no-unsafe-return': 'off', }, }, { // CommonJS test fixtures exist specifically to exercise require(). files: ['**/*.cjs'], rules: { 'typescript/no-require-imports': 'off', }, }, ], }, test: { testTimeout: 90_000, hookTimeout: 90_000, }, pack: { entry: ['src/index.ts'], format: ['esm', 'cjs'], dts: { oxc: true }, // Match the historical unbuild output layout referenced by package.json // (index.mjs + index.d.ts for ESM, index.cjs + index.d.cts for CJS). outExtensions: ({ format }) => (format === 'es' ? { js: '.mjs', dts: '.d.ts' } : { js: '.cjs', dts: '.d.cts' }), }, }); export default config;